diff --git a/projects/gnoland/gno.land/p/eve000/event/agenda/agenda.gno b/projects/gnoland/gno.land/p/eve000/event/agenda/agenda.gno index c15c70a..f44d828 100644 --- a/projects/gnoland/gno.land/p/eve000/event/agenda/agenda.gno +++ b/projects/gnoland/gno.land/p/eve000/event/agenda/agenda.gno @@ -11,18 +11,25 @@ import ( ) type Agenda struct { - Name string - Location *location.Location - StartDate time.Time - EndDate time.Time - Banner *component.Content - Description string - Sessions []*session.Session - renderOpts *component.RenderOpts + Name string + Location *location.Location + StartDate time.Time + EndDate time.Time + Banner *component.Content + Description string + Sessions []*session.Session + renderOpts *component.RenderOpts } var _ component.Component = (*Agenda)(nil) +func FormatDate(date time.Time) string { + if date.IsZero() { + return "" + } + return date.Format(component.DtFmt) +} + func (a *Agenda) SetBanner(heading *component.Content) { a.Banner = heading } @@ -37,10 +44,10 @@ func (a *Agenda) ToMarkdown() string { if a.RenderOpts().Location { markdown += "### " + a.Location.Name + "\n\n" } - if a.StartDate.Format(component.DtFmt) == a.EndDate.Format(component.DtFmt) { - markdown += a.StartDate.Format(component.DtFmt) + "\n\n" + if FormatDate(a.StartDate) == FormatDate(a.EndDate) { + markdown += FormatDate(a.StartDate) + "\n\n" } else { - markdown += a.StartDate.Format(component.DtFmt) + " - " + a.EndDate.Format(component.DtFmt) + "\n\n" + markdown += FormatDate(a.StartDate) + " - " + FormatDate(a.EndDate) + "\n\n" } markdown += a.Description + "\n\n" @@ -53,7 +60,7 @@ func (a *Agenda) ToMarkdown() string { // Group sessions by date sessionsByDate := make(map[string][]*session.Session) for _, session := range a.Sessions { - date := session.StartTime.Format(component.DtFmt) + date := FormatDate(session.StartTime) sessionsByDate[date] = append(sessionsByDate[date], session) } @@ -100,8 +107,8 @@ func (a *Agenda) ToJson() string { json := "{\n" json += "\"Name\":\"" + a.Name + "\",\n" json += "\"Location\":" + a.Location.ToJson() + ",\n" - json += "\"StartDate\":\"" + a.StartDate.Format(time.RFC3339) + "\",\n" - json += "\"EndDate\":\"" + a.EndDate.Format(time.RFC3339) + "\",\n" + json += "\"StartDate\":\"" + FormatDate(a.StartDate) + "\",\n" + json += "\"EndDate\":\"" + FormatDate(a.EndDate) + "\",\n" json += "\"Description\":\"" + a.Description + "\",\n" json += "\"Sessions\":[\n" for i, session := range a.Sessions { @@ -127,10 +134,10 @@ func (a *Agenda) ToSVGFragment(y *int) string { *y += 10 svg += component.RenderSVGLine(y, "subtitle", "", a.Location.Name) *y += 10 - if a.StartDate.Format(component.DtFmt) == a.EndDate.Format(component.DtFmt) { - svg += component.RenderSVGLine(y, "text", "", a.StartDate.Format(component.DtFmt)) + if a.StartDate == a.EndDate { + svg += component.RenderSVGLine(y, "text", "", FormatDate(a.StartDate)) } else { - svg += component.RenderSVGLine(y, "text", "", a.StartDate.Format(component.DtFmt)+" - "+a.EndDate.Format(component.DtFmt)) + svg += component.RenderSVGLine(y, "text", "", FormatDate(a.StartDate) + " - " + FormatDate(a.EndDate)) } *y += 20 svg += component.RenderSVGLine(y, "text", "", a.Description) diff --git a/projects/gnoland/gno.land/p/eve000/event/component/component.gno b/projects/gnoland/gno.land/p/eve000/event/component/component.gno index 5a50419..5bccb0d 100644 --- a/projects/gnoland/gno.land/p/eve000/event/component/component.gno +++ b/projects/gnoland/gno.land/p/eve000/event/component/component.gno @@ -6,6 +6,7 @@ import ( "strings" "time" + "gno.land/p/moul/txlink" "gno.land/p/demo/ufmt" ) @@ -13,16 +14,13 @@ const DtFmt = "Mon Jan 2" type Component interface { ToAnchor() string - ToMarkdown() string // REVIEW: should we allow filtering? + ToMarkdown() string ToJson() string ToSVG() string ToSvgDataUrl() string - RenderOpts() *RenderOpts + RenderOpts() *RenderOpts // REVIEW: should we remove renderOpts? we could add OnlineEvent vs InPersonEvent (see schema.org) } -// RenderOpts is a "feature flag" struct for controlling the rendering of components. -// It allows for selective rendering of different parts of a component. -// REVIEW: consider using a new set of render opts per event rather than 1-per-registry as it is now type RenderOpts struct { Svg bool Schedule bool @@ -217,6 +215,10 @@ func escapeHtml(s string) string { return s } +func TxlinkButton(label, method string) string { + return Button(label, txlink.NewLink(method).URL()) +} + func Button(label, path string) string { return SubmitButton(label, path, 16, 120) // Default font size and min width } diff --git a/projects/gnoland/gno.land/p/eve000/event/event.gno b/projects/gnoland/gno.land/p/eve000/event/event.gno index d1734e6..fa7ec59 100644 --- a/projects/gnoland/gno.land/p/eve000/event/event.gno +++ b/projects/gnoland/gno.land/p/eve000/event/event.gno @@ -1,6 +1,7 @@ package event import ( + "std" "strconv" "time" @@ -13,6 +14,39 @@ import ( "gno.land/p/eve000/event/speaker" ) +type Api interface { + AddOrganizer(addr std.Address) + AddProposer(addr, sender std.Address) + AddReviewer(addr, sender std.Address) + AddSpeaker(addr std.Address) + AdminRemoveRole(role string, addr std.Address) + AdminSetRole(role string, addr std.Address) + AssertAtLeastRole(role string, sender std.Address) + Destroy(markdown string) + HasRole(role string, addr std.Address) bool + JoinAsAttendee() + JoinWaitlist() + ListRoles() []string + Publish(markdown string) + RegisterEvent(evt *Event, opts *component.RenderOpts) string + RemoveOrganizer(addr std.Address) + RemoveProposer(addr, sender std.Address) + RemoveReviewer(addr, sender std.Address) + RemoveSelfAsAttendee() + RemoveSelfFromWaitlist() + RemoveSpeaker(addr std.Address) + Render(path string) string + RenderAcl(path string) string + RenderList(role string) string + ResetRoles() + RoleExists(role string) bool + SetContent(key, markdown string) + SetPatchLevel(level int) + SetRoleHandler(role string, fn func(string) bool) + Unpublish(key string) + UnsetRoleHandler(role string) +} + var f = ufmt.Sprintf type Storage struct { diff --git a/projects/gnoland/gno.land/p/eve000/event/location/location.gno b/projects/gnoland/gno.land/p/eve000/event/location/location.gno index 3deeb3c..f1da69e 100644 --- a/projects/gnoland/gno.land/p/eve000/event/location/location.gno +++ b/projects/gnoland/gno.land/p/eve000/event/location/location.gno @@ -33,8 +33,6 @@ func (l *Location) SetDescription(description string) { l.Description = description } -// REVIEW: need setters for tags and renderOpts? - func (l *Location) ToAnchor() string { return component.StringToAnchor(l.Name) } diff --git a/projects/gnoland/gno.land/p/eve000/event/register/register.gno b/projects/gnoland/gno.land/p/eve000/event/register/register.gno index 5711f1b..46e1542 100644 --- a/projects/gnoland/gno.land/p/eve000/event/register/register.gno +++ b/projects/gnoland/gno.land/p/eve000/event/register/register.gno @@ -1,7 +1,9 @@ package register import ( + "std" "strconv" + "strings" "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" @@ -12,7 +14,32 @@ import ( type Registry struct { Events *avl.Tree LiveEventId string - renderOpts *component.RenderOpts + RenderOpts *component.RenderOpts + patchLevel int // current patch level, used for debugging and content management + patchRealm string // realm that is allowed to update the patch level, used for debugging and content management +} + +func NewRegistry(renderOpts *component.RenderOpts) *Registry { + return &Registry{ + Events: avl.NewTree(), + LiveEventId: "", + RenderOpts: renderOpts, + patchLevel: 0, + patchRealm: "", + } +} + +func (r *Registry) GetPatchLevel() string { + realmLink := strings.TrimPrefix(r.patchRealm, "gno.land") + return ufmt.Sprintf("[rev %d](%s)", r.patchLevel, realmLink) +} + +func (r *Registry) SetPatchLevel(level int) { + if r.patchLevel+1 != level { + panic("patch level must be incremented by 1, current: " + strconv.Itoa(r.patchLevel) + ", new: " + strconv.Itoa(level)) + } + r.patchRealm = std.CurrentRealm().PkgPath() + r.patchLevel = level } func (r *Registry) GetEvent(id string) *event.Event { @@ -24,7 +51,7 @@ func (r *Registry) GetEvent(id string) *event.Event { } func (r *Registry) SetRenderOpts(opts *component.RenderOpts) { - r.renderOpts = opts + r.RenderOpts = opts } func (r *Registry) String() string { diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/api.gno b/projects/gnoland/gno.land/r/buidlthefuture000/api.gno new file mode 100644 index 0000000..a99518d --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/api.gno @@ -0,0 +1,68 @@ +package events + +import ( + "std" + "strings" + + "gno.land/p/demo/avl" +) + +var ( + api = &App{} + registry = avl.NewTree() + displayPaths []string + realmAllowPrefix []string +) + +func init() { + realmAllowPrefix = append(realmAllowPrefix, std.CurrentRealm().PkgPath()+"/") // must be in realm sub-path +} + +func hasAllowedPrefix() bool { + prevRealm := std.PreviousRealm().PkgPath() + for _, callerPath := range realmAllowPrefix { + if strings.HasPrefix(prevRealm, callerPath) { + return true + } + } + return prevRealm == "" +} + +func assertAccess() { + if !hasAllowedPrefix() { + panic("access denied: " + std.PreviousRealm().PkgPath() + + " realm must match an allowed prefix:[" + strings.Join(realmAllowPrefix, ",") + "]") + } +} + +func Render(path string) string { + return api.Render(path) +} + +// Register registers a ContentBlock function - use this to override the default rendering +func Register(key string, block func(path string) string) { + api.Register(key, block) +} + +type App struct{} + +func (*App) Register(key string, block func(path string) string) { + assertAccess() + if _, ok := registry.Get(key); ok { + // TODO emit update event + } + registry.Set(key, block) +} + +func (*App) Render(path string) string { + sb := strings.Builder{} + for _, key := range displayPaths { + if block, ok := registry.Get(key); ok { + sb.WriteString("### " + key + "\n") + sb.WriteString(block.(func(path string) string)(path)) + // TODO: also render TermSet + sb.WriteString("\n") + } + } + return sb.String() +} diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/acl.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/acl.gno new file mode 100644 index 0000000..55480d6 --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/acl.gno @@ -0,0 +1,31 @@ +package events + +import( + "std" + "strings" +) + +var ( + realmAllowPrefix []string +) + +func init() { + realmAllowPrefix = append(realmAllowPrefix, std.CurrentRealm().PkgPath()+"/") // must be in realm sub-path +} + +func hasAllowedPrefix() bool { + prevRealm := std.PreviousRealm().PkgPath() + for _, callerPath := range realmAllowPrefix { + if strings.HasPrefix(prevRealm, callerPath) { + return true + } + } + return prevRealm == "" +} + +func assertAccess() { + if !hasAllowedPrefix() { + panic("access denied: " + std.PreviousRealm().PkgPath() + + " realm must match an allowed prefix:[" + strings.Join(realmAllowPrefix, ",") + "]") + } +} \ No newline at end of file diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/api.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/api.gno index 7c54b3b..2f1ba95 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/api.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/api.gno @@ -2,89 +2,200 @@ package events import ( "std" - "strings" + "gno.land/p/demo/avl" + "gno.land/p/eve000/event" + "gno.land/p/eve000/event/component" ) var ( - registry = avl.NewTree() // registry of ContentBlock functions - displayPaths []string - realmAllowPrefix []string + featuredRealm = "" + eventRealms = avl.NewTree() ) -func init() { - allowPatchesFromSubRealms() +// SetFeaturedRealm sets the featured realm for the events package. +func SetFeaturedRealm(realm string) { + assertAccess() + if realm == "" { + panic("cannot set empty featured realm") + } + featuredRealm = realm } -type ContentBlock = func(path string) string - -func allowPatchesFromSubRealms() { - realmAllowPrefix = append(realmAllowPrefix, std.CurrentRealm().PkgPath()+"/") // must be in realm sub-path +// AddEventRealm adds an event realm to the global eventRealms tree. +func AddEventRealm(eveApi event.Api) { + assertAccess() + path := std.CurrentRealm().PkgPath() + if path == "" { + panic("cannot add event realm with empty path") + } + eventRealms.Set(path, eveApi) } -func hasAllowedPrefix() bool { - prevRealm := std.PreviousRealm() - for _, callerPath := range realmAllowPrefix { - if strings.HasPrefix(prevRealm.PkgPath(), callerPath) { - return true - } +// Eve returns event.Api for a given realm path. +func Eve(realmPath string) event.Api { + evt, ok := eventRealms.Get(realmPath) + if !ok { + panic("no event realm found for the given path") } - return false + app, ok := evt.(event.Api) + if !ok { + panic("event is not an EveApi") + } + return app } -func assertAccess() { - if !hasAllowedPrefix() { - panic("access denied: " + std.PreviousRealm().PkgPath() + - " realm must match an allowed prefix:[" + strings.Join(realmAllowPrefix, ",") + "]") - } +// AddOrganizer adds an organizer to the event realm. +func AddOrganizer(realmPath string, addr std.Address) { + Eve(realmPath).AddOrganizer(addr) } -func Register(key string, block ContentBlock) { - assertAccess() - register(key, block) + +// AddProposer adds a proposer to the event realm. +func AddProposer(realmPath string, addr, sender std.Address) { + Eve(realmPath).AddProposer(addr, sender) } -func register(key string, block ContentBlock) { - if _, ok := registry.Get(key); ok { - // TODO emit update event - } - registry.Set(key, block) +// AddReviewer adds a reviewer to the event realm. +func AddReviewer(realmPath string, addr, sender std.Address) { + Eve(realmPath).AddReviewer(addr, sender) } -func Publish(keys ...string) { - assertAccess() - if len(keys) == 0 { - panic("no keys provided to Publish") - } - publish(keys...) +// AddSpeaker adds a speaker to the event realm. +func AddSpeaker(realmPath string, addr std.Address) { + Eve(realmPath).AddSpeaker(addr) } -func publish(keys ...string) { - for _, key := range keys { - if block, ok := registry.Get(key); ok { - block.(ContentBlock)("") // test call with empty path - } else { - panic("Key not found: " + key) - } - } - displayPaths = keys +// AdminAddRole adds a role to the event realm for a given address. +func AdminRemoveRole(realmPath string, role string, addr std.Address) { + Eve(realmPath).AdminRemoveRole(role, addr) } -func Content(key string, path string) string { - if block, ok := registry.Get(key); ok { - return block.(ContentBlock)(path) - } - panic("Key not found: " + key) -} - -func Render(path string) string { - sb := strings.Builder{} - for _, key := range displayPaths { - if block, ok := registry.Get(key); ok { - sb.WriteString("### "+key+"\n") - sb.WriteString(block.(ContentBlock)(path)) - // TODO: also render TermSet - sb.WriteString("\n") - } - } - return sb.String() -} \ No newline at end of file +// AdminSetRole sets a role for a given address in the event realm. +func AdminSetRole(realmPath string, role string, addr std.Address) { + Eve(realmPath).AdminSetRole(role, addr) +} + +// FIXME: remove this deprecated func +func AssertAtLeastRole(realmPath string, role string, sender std.Address) { + Eve(realmPath).AssertAtLeastRole(role, sender) +} + +// Destroy removes all data in the event realm and publishes a markdown message in it's place. +func Destroy(realmPath string, markdown string) { + Eve(realmPath).Destroy(markdown) +} + +// HasRole checks if the given address has the specified role in the event realm. +func HasRole(realmPath string, role string, addr std.Address) bool { + return Eve(realmPath).HasRole(role, addr) +} + +// JoinAsOrganizer allows the sender to join the event realm as an organizer. +func JoinAsAttendee(realmPath string) { + Eve(realmPath).JoinAsAttendee() +} + +// JoinWaitlist allows the sender to join the event realm's waitlist. +func JoinWaitlist(realmPath string) { + Eve(realmPath).JoinWaitlist() +} + +// ListRoles returns a list of roles in the event realm. +func ListRoles(realmPath string) []string { + return Eve(realmPath).ListRoles() +} + +// Publish publishes a markdown message in the event realm. +func Publish(realmPath string, markdown string) { + Eve(realmPath).Publish(markdown) +} + +// RegisterEvent registers an event in the event realm with the given options. +func RegisterEvent(realmPath string, evt *event.Event, opts *component.RenderOpts) string { + return Eve(realmPath).RegisterEvent(evt, opts) +} + +// RemoveOrganizer removes an organizer from the event realm. +func RemoveOrganizer(realmPath string, addr std.Address) { + Eve(realmPath).RemoveOrganizer(addr) +} + +// RemoveProposer removes a proposer from the event realm. +func RemoveProposer(realmPath string, addr, sender std.Address) { + Eve(realmPath).RemoveProposer(addr, sender) +} + +// RemoveReviewer removes a reviewer from the event realm. +func RemoveReviewer(realmPath string, addr, sender std.Address) { + Eve(realmPath).RemoveReviewer(addr, sender) +} + +// RemoveSelfAsAttendee removes the sender from the event realm as an attendee. +func RemoveSelfAsAttendee(realmPath string) { + Eve(realmPath).RemoveSelfAsAttendee() +} + +// RemoveSelfFromWaitlist removes the sender from the event realm's waitlist. +func RemoveSelfFromWaitlist(realmPath string) { + Eve(realmPath).RemoveSelfFromWaitlist() +} + +// RemoveSpeaker removes a speaker from the event realm. +func RemoveSpeaker(realmPath string, addr std.Address) { + Eve(realmPath).RemoveSpeaker(addr) +} + +// RenderAcl renders the ACL for the given path in the event realm. +func RenderAcl(realmPath string, path string) string { + return Eve(realmPath).RenderAcl(path) +} + +// RenderList renders the list of events for the given role in the event realm. +func RenderList(realmPath string, role string) string { + return Eve(realmPath).RenderList(role) +} + +// ResetRoles resets all roles in the event realm. +func ResetRoles(realmPath string) { + Eve(realmPath).ResetRoles() +} + +// RoleExists checks if the specified role exists in the event realm. +func RoleExists(realmPath string, role string) bool { + return Eve(realmPath).RoleExists(role) +} + +// SetContent sets the content for a given key in the event realm. +func SetContent(realmPath string, key, markdown string) { + Eve(realmPath).SetContent(key, markdown) +} + +// SetPatchLevel sets the patch level for the event realm. +func SetPatchLevel(realmPath string, level int) { + Eve(realmPath).SetPatchLevel(level) +} + +// SetRoleHandler sets a handler function for a specific role in the event realm. +func SetRoleHandler(realmPath string, role string, fn func(string) bool) { + Eve(realmPath).SetRoleHandler(role, fn) +} + +// Unpublish removes the content associated with the given key in the event realm. +func Unpublish(realmPath string, key string) { + Eve(realmPath).Unpublish(key) +} + +// UnsetRoleHandler removes the handler function for a specific role in the event realm. +func UnsetRoleHandler(realmPath string, role string) { + Eve(realmPath).UnsetRoleHandler(role) +} + +// Register registers a ContentBlock function - use this to override the default rendering +func Register(key string, block func(string) string) { + app.Register(key, block) +} + +// Render renders the content for the given path using the registered ContentBlock function. +func Render(path string) (out string) { + return app.Render(path) +} diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/app.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/app.gno new file mode 100644 index 0000000..3cb2fe0 --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/app.gno @@ -0,0 +1,36 @@ +package events + +import ( + "strings" + + "gno.land/p/demo/avl" +) + +var ( + app = &App{} + registry = avl.NewTree() // registry of ContentBlock functions + displayPaths []string +) + +type App struct{} + +func (*App) Register(key string, block func(string) string) { + assertAccess() + if _, ok := registry.Get(key); ok { + // TODO emit update event + } + registry.Set(key, block) +} + +func (*App) Render(path string) string { + sb := strings.Builder{} + for _, key := range displayPaths { + if block, ok := registry.Get(key); ok { + sb.WriteString("### " + key + "\n") + sb.WriteString(block.(func(string) string)(path)) + // TODO: also render TermSet + sb.WriteString("\n") + } + } + return sb.String() +} diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/acl.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/acl.gno index 2c69710..683019e 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/acl.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/acl.gno @@ -1,4 +1,4 @@ -package event +package gnolandlaunch import ( "std" @@ -6,9 +6,33 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" + "gno.land/p/moul/txlink" ) +func init() { + realmAllowPrefix = append(realmAllowPrefix, std.CurrentRealm().PkgPath()+"/patch") +} + +func hasAllowedPrefix() bool { + currentRealm := std.CurrentRealm() + for _, callerPath := range realmAllowPrefix { + if strings.HasPrefix(currentRealm.PkgPath(), callerPath) { + return true + } + } + return false +} + +// assertAccess is used to restrict access to certain functions intended to be used by patch-realm admins. +func assertAccess() { + if !hasAllowedPrefix() { + panic("access denied: " + std.CurrentRealm().PkgPath() + + " realm must match an allowed prefix:[" + strings.Join(realmAllowPrefix, ",") + "]") + } +} + var ( + acl = &Acl{} waitlist *avl.Tree organizers *avl.Tree speakers *avl.Tree @@ -19,6 +43,8 @@ var ( aclOverride map[string]bool ) +type Acl struct{} + const ( AssertIsOrganizerMsg string = "error: The PreviousRealm Address must be in the organizer's group execute this function." AssertOrganizerNotRemoveSelfMsg string = "error: an organizer cannot remove itself." @@ -68,8 +94,8 @@ func init() { } // Use Admin access to set the role of an address. -func AdminSetRole(role string, addr std.Address) { - AssertAdminAccess() +func (*Acl) AdminSetRole(role string, addr std.Address) { + assertAccess() switch role { case "waitlist": waitlist.Set(string(addr), struct{}{}) @@ -89,8 +115,8 @@ func AdminSetRole(role string, addr std.Address) { } // AdminRemoveRole removes the address from the specified role. -func AdminRemoveRole(role string, addr std.Address) { - AssertAdminAccess() +func (*Acl) AdminRemoveRole(role string, addr std.Address) { + assertAccess() switch role { case "waitlist": waitlist.Remove(string(addr)) @@ -110,8 +136,8 @@ func AdminRemoveRole(role string, addr std.Address) { } // ResetRoles resets all roles to empty trees and re-initializes the accessChecks map. -func ResetRoles() { - AssertAdminAccess() +func (*Acl) ResetRoles() { + assertAccess() waitlist = avl.NewTree() organizers = avl.NewTree() speakers = avl.NewTree() @@ -130,49 +156,49 @@ func ResetRoles() { // JoinWaitlist sets the address of the previous realm as an waitlist attendee // for the event. -func JoinWaitlist() { +func (*Acl) JoinWaitlist() { waitlist.Set(string(std.PreviousRealm().Address()), struct{}{}) } // RemoveSelfFromWaitlist removes the address of the previous realm as a waitlist attendee // for the event. -func RemoveSelfFromWaitlist() { +func (*Acl) RemoveSelfFromWaitlist() { waitlist.Remove(string(std.PreviousRealm().Address())) } // JoinAsAttendee sets the address of the previous realm as an attendee // for the event. -func JoinAsAttendee() { +func (*Acl) JoinAsAttendee() { attendees.Set(string(std.PreviousRealm().Address()), struct{}{}) } // RemoveSelfAsAttendee removes the address of the previous realm as an attendee // for the event. -func RemoveSelfAsAttendee() { +func (*Acl) RemoveSelfAsAttendee() { attendees.Remove(string(std.PreviousRealm().Address())) } // AddSpeaker takes a std.Address and adds it to the speaker tree if the caller is an organizer. -func AddSpeaker(addr std.Address) { +func (*Acl) AddSpeaker(addr std.Address) { assertIsOrganizer() speakers.Set(string(addr), struct{}{}) } // RemoveSpeaker takes an std.Address and removes it from the speaker tree if the call is an organizer. -func RemoveSpeaker(addr std.Address) { +func (*Acl) RemoveSpeaker(addr std.Address) { assertIsOrganizer() speakers.Remove(string(addr)) } // AddOrganizer takes a std.Address and adds it to the speaker tree if the caller is an organizer. -func AddOrganizer(addr std.Address) { +func (*Acl) AddOrganizer(addr std.Address) { assertIsOrganizer() organizers.Set(string(addr), struct{}{}) } // RemoveOrganizer takes an std.Address and removes it from the speaker tree if the call is an organizer and // the caller isn't trying to remove itself. -func RemoveOrganizer(addr std.Address) { +func (*Acl) RemoveOrganizer(addr std.Address) { assertIsOrganizer() if addr == std.PreviousRealm().Address() { panic(AssertOrganizerNotRemoveSelfMsg) @@ -188,29 +214,29 @@ func assertIsOrganizer() { } // Public API for organizers to manage proposers and reviewers -func AddProposer(addr, sender std.Address) { - AssertAtLeastRole("organizer", sender) +func (*Acl) AddProposer(addr, sender std.Address) { + acl.AssertAtLeastRole("organizer", sender) proposers.Set(string(addr), struct{}{}) } -func RemoveProposer(addr, sender std.Address) { - AssertAtLeastRole("organizer", sender) +func (*Acl) RemoveProposer(addr, sender std.Address) { + acl.AssertAtLeastRole("organizer", sender) proposers.Remove(string(addr)) } -func AddReviewer(addr, sender std.Address) { - AssertAtLeastRole("organizer", sender) +func (*Acl) AddReviewer(addr, sender std.Address) { + acl.AssertAtLeastRole("organizer", sender) reviewers.Set(string(addr), struct{}{}) } -func RemoveReviewer(addr, sender std.Address) { - AssertAtLeastRole("organizer", sender) +func (*Acl) RemoveReviewer(addr, sender std.Address) { + acl.AssertAtLeastRole("organizer", sender) reviewers.Remove(string(addr)) } // RoleExists takes a role and returns a boolean value that indicates whether or not // that role exists in the accessChecks map. It returns false if any other role type is requested. -func RoleExists(role string) bool { +func (*Acl) RoleExists(role string) bool { if _, ok := accessChecks[role]; ok { return true } @@ -218,9 +244,7 @@ func RoleExists(role string) bool { } // HasRole takes a role and an address and returns a boolean value that indicates whether or not -// that address is a member of one of three supported roles, `attendee`, `speaker`, and `organizer`. -// It returns false if any other role type is requested. -func HasRole(role string, addr std.Address) bool { +func (*Acl) HasRole(role string, addr std.Address) bool { if fn, ok := accessChecks[role]; ok { return fn(string(addr)) } @@ -228,7 +252,7 @@ func HasRole(role string, addr std.Address) bool { } // ListRoles returns a slice of strings that contains all the roles in the accessChecks map. -func ListRoles() []string { +func (*Acl) ListRoles() []string { roles := make([]string, 0) for role := range accessChecks { roles = append(roles, role) @@ -237,29 +261,27 @@ func ListRoles() []string { } // SetRoleHandler takes a role and a function that takes an address and returns a boolean value. -func SetRoleHandler(role string, fn func(string) bool) { +func (*Acl) SetRoleHandler(role string, fn func(string) bool) { aclOverride[role] = true - AssertAdminAccess() + assertAccess() accessChecks[role] = fn } // UnsetRoleHandler takes a role and removes it from the accessChecks map. -func UnsetRoleHandler(role string) { - AssertAdminAccess() +func (*Acl) UnsetRoleHandler(role string) { + assertAccess() delete(accessChecks, role) } -func AssertAtLeastRole(role string, sender std.Address) { - if !HasRole(role, sender) { +func (*Acl) AssertAtLeastRole(role string, sender std.Address) { + if !acl.HasRole(role, sender) { panic(ufmt.Sprintf("address %s must have %s role or higher", sender, role)) } } // Render takes no arguments and displays a simple page showing up to 100 -// addresses per role group. -/* -func RenderAcl(path string) string { - id := getIdFromPath(path) +func (*Acl) Render(path string) string { + id := registry.LiveEventId evt := registry.GetEvent(id) if evt.Name == "" { panic(ufmt.Sprintf("error: event with id %d not found", id)) @@ -287,19 +309,18 @@ Note: This renderer will only display up to 100 addresses per group. txlink.NewLink("JoinAsAttendee").URL(), txlink.NewLink("RemoveSelfAsAttendee").URL())) - sb.WriteString("## Roles") + sb.WriteString("## Roles") for role, _ := range accessChecks { - list := renderList(role) + list := acl.RenderList(role) if list != "" { - sb.WriteString(ufmt.Sprintf("\n%s\n", renderList(role))) + sb.WriteString(ufmt.Sprintf("\n%s\n", acl.RenderList(role))) } } return sb.String() } -*/ -func renderList(role string) string { +func (*Acl) RenderList(role string) string { var sb strings.Builder sb.WriteString(ufmt.Sprintf("\n### %s \n", role)) var addrs []std.Address diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/api.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/api.gno deleted file mode 100644 index 5fdee1b..0000000 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/api.gno +++ /dev/null @@ -1,204 +0,0 @@ -package event - -import ( - "net/url" - "std" - "strings" - - "gno.land/p/demo/ufmt" - "gno.land/p/eve000/event/component" - "gno.land/p/eve000/event/register" - "gno.land/p/moul/txlink" -) - -var ( - staticContent = &component.Content{ - Published: false, - Markdown: "This page has been disabled", - } - eventMap = &component.Content{ - Published: false, - Markdown: "This page has been disabled", - } - - realmAllowPrefix = []string{} // realms prefixes that have access to update the registry - banner = &component.Content{ - Published: true, - Markdown: "\n\n#### Status: Accepting Session Proposals\n\n" + - "Join us on [discord](https://discord.gg/YFtMjWwUN7) to get access to submit a proposal.\n\n" + - "or\n\n" + txlinkButton("Register To Attend", "JoinWaitlist"), - } - renderOpts = &component.RenderOpts{ - Location: false, - Svg: true, - Schedule: true, - Speaker: false, - } - registry = ®ister.Registry{} -) - -func init() { - realmAllowPrefix = append(realmAllowPrefix, std.CurrentRealm().PkgPath()+"/patch") - - registry.SetRenderOpts(renderOpts) - id := registry.RegisterEvent(gnolandlaunch, renderOpts) - registry.LiveEventId = id -} - -func NotEmpty(value string) bool { - return value != "" -} - -func Render(path string) (out string) { - fullURL := std.CurrentRealm().PkgPath() + path - u, err := url.Parse(fullURL) - if err != nil { - panic("Error Parsing URL") - } - q := u.Query() - event_id := q.Get("event") - if event_id == "" { - event_id = registry.LiveEventId - } - - event := registry.GetEvent(event_id) - switch { - case NotEmpty(q.Get("session")): - return component.RenderComponent(path, event.GetSession(q.Get("session"))) - case NotEmpty(q.Get("location")): - return component.RenderComponent(path, event.GetLocation(q.Get("location"))) - case NotEmpty(q.Get("speaker")): - return component.RenderComponent(path, event.GetSpeaker(q.Get("speaker"))) - default: - agenda := event.Agenda() - agenda.SetBanner(banner) - return component.RenderComponent(path, agenda) - } -} - -// Info returns a formatted string with the message and path -// including the liveEventId for debugging purposes. -func info(opts ...string) string { - msg := "info: " - path := "" - if len(opts) == 1 { - msg = opts[0] - } else if len(opts) == 2 { - msg = opts[0] - path = opts[1] - } - return ufmt.Sprintf("%s\n\nliveEvent: %d\n\npath: %v", msg, registry.LiveEventId, path) -} - -/* Content Management API */ -/* -func Publish(markdown string) { - AssertAdminAccess() - staticContent.Published = true - staticContent.Markdown = markdown -} - -func Destroy(markdown string) { - Publish(markdown) - registry = register.NewRegistry(renderOpts) // reset the registry to a new instance -} - -/* -func Revision() string { - patchLevelStr := strconv.Itoa(eventRegistry.PatchLevel) - if eventRegistry.PatchLevel < 10 { - patchLevelStr = "00" + patchLevelStr - } else if eventRegistry.PatchLevel < 100 { - patchLevelStr = "0" + patchLevelStr - } - return "\n\n[rev: " + strconv.Itoa(eventRegistry.PatchLevel) + "](./gophercon/patch" + patchLevelStr + ")\n\n" -} -*/ - -func Unpublish(key string) { - AssertAdminAccess() - switch key { - case "map": - eventMap.Published = false - case "published": - staticContent.Published = false - case "banner": - banner.Published = false - default: - panic("invalid key: " + key) - } -} - -func SetContent(key, markdown string) { - AssertAdminAccess() - switch key { - case "map": - eventMap.SetPublished(true) - eventMap.SetMarkdown(markdown) - case "published": - staticContent.SetPublished(true) - staticContent.SetMarkdown(markdown) - case "banner": - banner.SetPublished(true) - banner.SetMarkdown(markdown) - default: - panic("invalid key: " + key) - } -} - -/* Render Management API */ -func ToggleOpts(opts ...string) { - AssertAdminAccess() - for _, opt := range opts { - switch opt { - case "location": - renderOpts.Location = !renderOpts.Location - case "svg": - renderOpts.Svg = !renderOpts.Svg - case "schedule": - renderOpts.Schedule = !renderOpts.Schedule - case "content": - staticContent.Published = !staticContent.Published - case "map": - eventMap.Published = !eventMap.Published - case "speaker": - renderOpts.Speaker = !renderOpts.Speaker - default: - panic("invalid option: " + opt) - } - } -} - -/* -func SetPatchLevel(level int) { - AssertAdminAccess() - if level != eventRegistry.PatchLevel+1 { - panic("patch level must be incremented by 1") - } - eventRegistry.SetPatchLevel(level) -} -*/ - -/* Auth API */ -func HasAllowedPrefix() bool { - currentRealm := std.CurrentRealm() - for _, callerPath := range realmAllowPrefix { - if strings.HasPrefix(currentRealm.PkgPath(), callerPath) { - return true - } - } - return false -} - -// AssertAdminAccess panics if the caller's realm does not match the allowed prefix. -// This is used to restrict access to certain functions intended to be used by patch-realm admins. -func AssertAdminAccess() { - if !HasAllowedPrefix() { - panic("access denied: " + std.CurrentRealm().PkgPath() + - " realm must match an allowed prefix:[" + strings.Join(realmAllowPrefix, ",") + "]") - } -} - -func txlinkButton(label, method string) string { - return component.Button(label, txlink.NewLink(method).URL()) -} diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/app.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/app.gno new file mode 100644 index 0000000..914d83b --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/app.gno @@ -0,0 +1,190 @@ +package gnolandlaunch + +import ( + "net/url" + "std" + + "gno.land/p/eve000/event" + "gno.land/p/eve000/event/component" + "gno.land/p/eve000/event/register" +) + +type App struct{} + +var ( + _ event.Api = (*App)(nil) + + app = &App{} + + staticContent = &component.Content{ + Published: false, + Markdown: "This page has been disabled", + } + eventMap = &component.Content{ + Published: false, + Markdown: "This page has been disabled", + } + + realmAllowPrefix = []string{} // realms prefixes that have access to update the registry + registry = ®ister.Registry{} +) + +func notEmpty(value string) bool { + return value != "" +} + +func Render(path string) (out string) { + return app.Render(path) + "\n\n" + registry.GetPatchLevel() + "\n\n" +} + +func (*App) Render(path string) (out string) { + fullURL := std.CurrentRealm().PkgPath() + path + u, err := url.Parse(fullURL) + if err != nil { + panic("Error Parsing URL") + } + q := u.Query() + event_id := q.Get("event") + if event_id == "" { + event_id = registry.LiveEventId + } + + event := registry.GetEvent(event_id) + switch { + case notEmpty(q.Get("session")): + return component.RenderComponent(path, event.GetSession(q.Get("session"))) + case notEmpty(q.Get("location")): + return component.RenderComponent(path, event.GetLocation(q.Get("location"))) + case notEmpty(q.Get("speaker")): + return component.RenderComponent(path, event.GetSpeaker(q.Get("speaker"))) + default: + agenda := event.Agenda() + agenda.SetBanner(banner) // REVIEW: is there a better way to inject the banner? + return component.RenderComponent(path, agenda) + } +} + +func (*App) Publish(markdown string) { + assertAccess() + staticContent.Published = true + staticContent.Markdown = markdown +} + +func (*App) Destroy(markdown string) { + app.Publish(markdown) + registry = register.NewRegistry(registry.RenderOpts) // reset the registry to a new instance +} + +func (*App) Unpublish(key string) { + assertAccess() + switch key { + case "map": + eventMap.Published = false + case "published": + staticContent.Published = false + case "banner": + banner.Published = false + default: + panic("invalid key: " + key) + } +} + +func (*App) SetContent(key, markdown string) { + assertAccess() + switch key { + case "map": + eventMap.SetPublished(true) + eventMap.SetMarkdown(markdown) + case "published": + staticContent.SetPublished(true) + staticContent.SetMarkdown(markdown) + case "banner": + banner.SetPublished(true) + banner.SetMarkdown(markdown) + default: + panic("invalid key: " + key) + } +} + +func (*App) SetPatchLevel(level int) { + assertAccess() + registry.SetPatchLevel(level) +} + +func (*App) RegisterEvent(evt *event.Event, opts *component.RenderOpts) string { + registry.SetRenderOpts(opts) // REVIEW: still needed? + id := registry.RegisterEvent(evt, opts) + registry.LiveEventId = id + return id +} +func (*App) AdminSetRole(role string, addr std.Address) { + acl.AdminSetRole(role, addr) +} + +func (*App) AdminRemoveRole(role string, addr std.Address) { + acl.AdminRemoveRole(role, addr) +} +func (*App) ResetRoles() { + acl.ResetRoles() +} +func (*App) JoinWaitlist() { + acl.JoinWaitlist() +} +func (*App) RemoveSelfFromWaitlist() { + acl.RemoveSelfFromWaitlist() +} +func (*App) JoinAsAttendee() { + acl.JoinAsAttendee() +} +func (*App) RemoveSelfAsAttendee() { + acl.RemoveSelfAsAttendee() +} +func (*App) AddSpeaker(addr std.Address) { + acl.AddSpeaker(addr) +} +func (*App) RemoveSpeaker(addr std.Address) { + acl.RemoveSpeaker(addr) +} +func (*App) AddOrganizer(addr std.Address) { + acl.AddOrganizer(addr) +} +func (*App) RemoveOrganizer(addr std.Address) { + acl.RemoveOrganizer(addr) +} +func (*App) AddProposer(addr, sender std.Address) { + acl.AddProposer(addr, sender) +} +func (*App) RemoveProposer(addr, sender std.Address) { + acl.RemoveProposer(addr, sender) +} +func (*App) AddReviewer(addr, sender std.Address) { + acl.AddReviewer(addr, sender) +} +func (*App) RemoveReviewer(addr, sender std.Address) { + acl.RemoveReviewer(addr, sender) +} +func (*App) RoleExists(role string) bool { + return acl.RoleExists(role) +} +func (*App) HasRole(role string, addr std.Address) bool { + return acl.HasRole(role, addr) +} +func (*App) ListRoles() []string { + return acl.ListRoles() +} +func (*App) SetRoleHandler(role string, fn func(string) bool) { + acl.SetRoleHandler(role, fn) +} +func (*App) UnsetRoleHandler(role string) { + acl.UnsetRoleHandler(role) +} +func (*App) AssertAtLeastRole(role string, sender std.Address) { + acl.AssertAtLeastRole(role, sender) +} +func (*App) RenderList(role string) string { + return acl.RenderList(role) +} + +func (*App) RenderAcl(path string) string { + return acl.Render(path) +} diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/event.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/event.gno index 392ff6e..c120114 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/event.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/event.gno @@ -1,14 +1,30 @@ -package event +package gnolandlaunch import ( "time" "gno.land/p/eve000/event" + "gno.land/p/eve000/event/component" "gno.land/p/eve000/event/location" "gno.land/p/eve000/event/session" "gno.land/p/eve000/event/speaker" ) +func init() { + app.RegisterEvent(gnolandlaunch, &component.RenderOpts{ + Location: false, + Svg: true, + Schedule: true, + Speaker: false, + }) +} + +var banner = &component.Content{ + Published: true, + Markdown: "\n\n#### Status: Accepting Session Proposals\n #### expected dates: Aug 2025 \n\n" + + component.TxlinkButton("Join Waitlist", "JoinWaitlist"), +} + var locations = map[string]*location.Location{ "gnowhere": { Name: "Gnowhere", @@ -94,15 +110,16 @@ var Sessions = map[string]*session.Session{ } var gnolandlaunch = &event.Event{ - Name: "Gnoland Launch", - Location: locations["gnowhere"], - StartDate: time.Date(2025, 6, 25, 0, 0, 0, 0, time.UTC), - EndDate: time.Date(2025, 6, 25, 23, 59, 59, 0, time.UTC), + Name: "Gnoland Launch", + Location: locations["gnowhere"], + // StartDate: time.Date(2025, 7, 25, 0, 0, 0, 0, time.UTC), + // EndDate: time.Date(2025, 7, 25, 23, 59, 59, 0, time.UTC), Description: "Join us as Gno.land creator Jae Kwon shares his vision of a logic-first internet—where code is law and realms are the new web.", - Sessions: []*session.Session{ - Sessions["keynote"], - Sessions["generics"], - Sessions["concurrency"], - Sessions["apis"], + Sessions: []*session.Session{ + // TODO: add finalized sessions here + // Sessions["keynote"], + // Sessions["generics"], + // Sessions["concurrency"], + // Sessions["apis"], }, } diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/gnomod.toml b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/gnomod.toml index f4a4723..0a46a2d 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/gnomod.toml +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/gnomod.toml @@ -1,2 +1,2 @@ module = "gno.land/r/buidlthefuture000/events/gnolandlaunch" -gno = "0.9" +gno = "0.9" \ No newline at end of file diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/patch001/patch.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/patch001/patch.gno index 28a2e17..9bece3a 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/patch001/patch.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/patch001/patch.gno @@ -1,41 +1,17 @@ package patch import ( - event "gno.land/r/buidlthefuture000/events/gnolandlaunch" + "gno.land/r/buidlthefuture000/events/gnolandlaunch" ) const releaseNotes = ` -### Patch 001 -- Speaker name updated -- Description updated -- Location name updated -- New session added -- Patch level set to 1 -- New event added -- New session added to new event -- New speaker added to new session -- New location added to new session +example patch 01 ` -// REVIEW: leaving patch 001 in the codebase for now as a reference -func init() { - newDescription := "GopherCon is a conference dedicated to the Go programming language, promoting its use," + - " fostering an inclusive community, and providing education to developers." - - // event.ToggleOpts("schedule") - - // event.SetContent("map", "\n\n{Map}") +var app = &gnolandlaunch.App{} - event.SetSpeakerName(2, "Alice Johnston") // overwrite speaker name adds a "t" to the last name - event.SetLocationName(2, "Room A-1") // overwrite location name - - eventId := event.LiveEventId() - event.SetEventDescription(eventId, newDescription) // overwrite description - removes "is an annual conference" and adds "is a conference" - newSessionId := event.AddEventSession(eventId, "1h30m", "30m") // add a new session with 30m delay - event.SetEventSessionSpeaker(0, newSessionId, eventId) // set the speaker for the new session to Alice Johnston - event.SetEventSessionDescription(0, 1, "Learn practical use cases for Go generics to simplify and enhance your code!") - - event.SetPatchLevel(1) // set the patch level to 1 +func init() { + app.SetPatchLevel(1) // set the patch level to 1 } func Render(_ string) string { diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/proposals/proposals.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/proposals/proposals.gno index 7181cee..6908941 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/proposals/proposals.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnolandlaunch/proposals/proposals.gno @@ -12,6 +12,8 @@ import ( "gno.land/p/eve000/event/proposal" ) +var api = &event.App{} + // Header for your Call for Proposals Realm var ProposalsRealmHeader = "# BTF 2025 Call for Proposals\n\n" @@ -138,8 +140,8 @@ func Render(path string) string { // --- API-Like Endpoints --- func SubmitProposal(title, abstract, topic, speaker string) { - - event.AssertAtLeastRole("proposer", std.OriginCaller()) + + api.AssertAtLeastRole("proposer", std.OriginCaller()) if topic, ok := EventTopics[topic]; ok { sp := &proposal.SubmittedProposal{ Title: title, @@ -154,8 +156,8 @@ func SubmitProposal(title, abstract, topic, speaker string) { } func ReviewProposal(title, comments, reviewer string, score int) { - - event.AssertAtLeastRole("reviewer", std.OriginCaller()) + + api.AssertAtLeastRole("reviewer", std.OriginCaller()) for i, p := range proposals { if p.GetTitle() == title { if sp, ok := p.(*proposal.SubmittedProposal); ok { @@ -172,8 +174,8 @@ func ReviewProposal(title, comments, reviewer string, score int) { } func ApproveProposal(title, approver string) { - - event.AssertAtLeastRole("organizer", std.OriginCaller()) + + api.AssertAtLeastRole("organizer", std.OriginCaller()) for i, p := range proposals { if p.GetTitle() == title { if rp, ok := p.(*proposal.ReviewedProposal); ok { @@ -188,20 +190,20 @@ func ApproveProposal(title, approver string) { } func ApproveProposals(titles []string, approver string) { - event.AssertAtLeastRole("organizer", std.OriginCaller()) + api.AssertAtLeastRole("organizer", std.OriginCaller()) for _, title := range titles { ApproveProposal(title, approver) } } func EditProposal(title, key, edit string) error { - + for _, p := range proposals { if p.GetTitle() != title { continue } - if !(event.HasRole("proposer", std.OriginCaller()) && std.OriginCaller().String() == p.GetOwner()) { + if !(api.HasRole("proposer", std.OriginCaller()) && std.OriginCaller().String() == p.GetOwner()) { return ufmt.Errorf("unauthorized: %q may not edit %q", std.OriginCaller(), p.GetTitle()) } @@ -265,7 +267,7 @@ func EditProposal(title, key, edit string) error { } func ExportApprovedProposals() []proposal.Proposal { - + var approved []proposal.Proposal for _, p := range proposals { if p.GetState() == proposal.Approved { diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnomod.toml b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnomod.toml index 8921259..90592e3 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/gnomod.toml +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/gnomod.toml @@ -1,2 +1,2 @@ module = "gno.land/r/buidlthefuture000/events" -gno = "0.9" +gno = "0.9" \ No newline at end of file diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/events/index.gno b/projects/gnoland/gno.land/r/buidlthefuture000/events/index.gno index 5d1320f..243ac14 100644 --- a/projects/gnoland/gno.land/r/buidlthefuture000/events/index.gno +++ b/projects/gnoland/gno.land/r/buidlthefuture000/events/index.gno @@ -5,17 +5,17 @@ import ( ) func addBlock(key, body string) { - register(key, func(_ string) string { - return body - }) - displayPaths = append(displayPaths, key) + app.Register(key, func(_ string) string { + return body + }) + displayPaths = append(displayPaths, key) } func init() { - addBlock("Buidl the Future", intro) - addBlock("Upcoming", latestEvent) - addBlock("Contribute", contact) - addBlock("Topics", topics) + addBlock("Buidl the Future", intro) + addBlock("Upcoming", latestEvent) + addBlock("Contribute", contact) + addBlock("Topics", topics) } var latestEvent = ` diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/gnomod.toml b/projects/gnoland/gno.land/r/buidlthefuture000/gnomod.toml new file mode 100644 index 0000000..7ede591 --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/gnomod.toml @@ -0,0 +1,2 @@ +module = "gno.land/r/buidlthefuture000" +gno = "0.9" \ No newline at end of file diff --git a/projects/gnoland/gno.land/r/buidlthefuture000/index.gno b/projects/gnoland/gno.land/r/buidlthefuture000/index.gno new file mode 100644 index 0000000..2fa2924 --- /dev/null +++ b/projects/gnoland/gno.land/r/buidlthefuture000/index.gno @@ -0,0 +1,22 @@ +package events + +func addBlock(key, body string) { + api.Register(key, func(_ string) string { + return body + }) + displayPaths = append(displayPaths, key) +} + +func init() { + addBlock("Buidl the Future", intro) +} + +var intro = ` +This realm contains the BUIDL THE FUTURE (BTF) project. +* 💬 [Discord](https://discord.gg/YFtMjWwUN7) Join us on discord. +* 📅 [/r/buidlthefuture000/events](/r/buidlthefuture000/events) - on-chain events hosted by BTF +* 🧩 [/p/eve000/event](/p/eve000) - eve000 provides a gnolang component library for events +* 🛠️ [GitHub](https://github.com/BUIDLTHEFUTURE/events) - report issues & contribute to the project +* 📦 [CDN files](https://cdn.jsdelivr.net/gh/BUIDLTHEFUTURE/events@main/static/) - static file hosting via jsDelivr +* 🧪 [AIBLabs](https://github.com/allinbits/labs) - review AIB labs projects still in incubation +` diff --git a/projects/gnoland/gno.land/r/linker000/mockevent/v1/mockevent.gno b/projects/gnoland/gno.land/r/linker000/mockevent/v1/mockevent.gno index 02206c8..ae7ad1a 100644 --- a/projects/gnoland/gno.land/r/linker000/mockevent/v1/mockevent.gno +++ b/projects/gnoland/gno.land/r/linker000/mockevent/v1/mockevent.gno @@ -35,34 +35,34 @@ func init() { // JoinAsAttendee sets the address of the previous realm as an attendee // for the event. func JoinAsAttendee() { - // + // attendees.Set(string(std.PreviousRealm().Address()), struct{}{}) } // RemoveSelfAsAttendee removes the address of the previous realm as an attendee // for the event. func RemoveSelfAsAttendee() { - // + // attendees.Remove(string(std.PreviousRealm().Address())) } // AddSpeaker takes a std.Address and adds it to the speaker tree if the caller is an organizer. func AddSpeaker(addr std.Address) { - // + // assertIsOrganizer() speakers.Set(string(addr), struct{}{}) } // RemoveSpeaker takes an std.Address and removes it from the speaker tree if the call is an organizer. func RemoveSpeaker(addr std.Address) { - // + // assertIsOrganizer() speakers.Remove(string(addr)) } // AddOrganizer takes a std.Address and adds it to the speaker tree if the caller is an organizer. func AddOrganizer(addr std.Address) { - // + // assertIsOrganizer() organizers.Set(string(addr), struct{}{}) } @@ -70,7 +70,7 @@ func AddOrganizer(addr std.Address) { // RemoveOrganizer takes an std.Address and removes it from the speaker tree if the call is an organizer and // the caller isn't trying to remove itself. func RemoveOrganizer(addr std.Address) { - // + // assertIsOrganizer() if addr == std.PreviousRealm().Address() { panic(AssertOrganizerNotRemoveSelfMsg)