Skip to content

gnocal working + gnofmt #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 30 additions & 49 deletions projects/gnocal/gnocal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ var f = fmt.Sprintf
//go:embed static/*
var static embed.FS

// Template pre-parsing
var (
tmplConnectionRefused = mustParseTemplate("connection_refused.html")
tmplInvalidRealmPath = mustParseTemplate("invalid_realm_path.html")
tmplRenderCalNotDeclared = mustParseTemplate("rendercal_not_declared.html")
tmplNoRenderDefined = mustParseTemplate("no_render_defined.html")
tmplUnknownRenderError = mustParseTemplate("unknown_render_error.html")
tmplLandingPage = mustParseTemplate("landing_page.html")
)

type Server struct {
router *chi.Mux
gnoClient *gnoclient.Client
Expand Down Expand Up @@ -60,96 +70,67 @@ func (s *Server) Run() error {
}

func (s *Server) RenderCalFromRealm(w http.ResponseWriter, r *http.Request) {
//gnocal.com/gno.land/r/buidlthefuture000/events/gnolandlaunch/calendar?format=ics
//gnocal.com/gno.land/r/buidlthefuture000/events/gnolandlaunch/calendar
calendarPath := chi.URLParam(r, "*")
if calendarPath == "" {
http.Error(w, "missing realm path", http.StatusBadRequest)
return
}

path := strconv.Quote("?" + r.URL.RawQuery) // e.g. "?" + "apple=2&session=1&session=100&session=0&format=json"
path := strconv.Quote("?" + r.URL.RawQuery)
stringToken, _, err := s.gnoClient.QEval(calendarPath, f(`RenderCal(%s)`, path))
if err != nil {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusInternalServerError)

errStr := err.Error()
switch {

case strings.Contains(errStr, "connect: connection refused"):
tmpl, parseErr := template.ParseFS(static, "static/connection_refused.html")
if parseErr != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
}
tmpl.Execute(w, map[string]string{
tmplConnectionRefused.Execute(w, map[string]string{
"RpcUrl": s.config.GnolandRpcUrl,
})
return

case strings.Contains(errStr, "invalid package path"):
tmpl, parseErr := template.ParseFS(static, "static/invalid_realm_path.html")
if parseErr != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
}
tmpl.Execute(w, map[string]string{
tmplInvalidRealmPath.Execute(w, map[string]string{
"InputPath": calendarPath,
})
return

case strings.Contains(errStr, "name RenderCal not declared"):
if _, _, renderErr := s.gnoClient.QEval(calendarPath, `Render("")`); renderErr == nil {
tmpl, parseErr := template.ParseFS(static, "static/rendercal_not_declared.html")
if parseErr != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
}
tmpl.Execute(w, map[string]string{
tmplRenderCalNotDeclared.Execute(w, map[string]string{
"RealmPath": calendarPath,
})
return
}
tmpl, parseErr := template.ParseFS(static, "static/no_render_defined.html")
if parseErr != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
} else {
tmplNoRenderDefined.Execute(w, nil)
}
tmpl.Execute(w, nil)
return

default:
tmpl, parseErr := template.ParseFS(static, "static/unknown_render_error.html")
if parseErr != nil {
http.Error(w, "Template error", http.StatusInternalServerError)
return
}
tmpl.Execute(w, map[string]string{
tmplUnknownRenderError.Execute(w, map[string]string{
"InputPath": calendarPath,
"ErrorMessage": errStr,
})
return
}
return
}

var out string // TODO: this is output post-processing, should be refactored out w/ better design
var out string
if removedLParen, cutPrefix := strings.CutPrefix(stringToken, `("`); cutPrefix {
out = removedLParen
}
if removedRParen, cutSuffix := strings.CutSuffix(out, `" string)`); cutSuffix {
out = removedRParen
}

w.Header().Set("Content-Type", "text/calendar; charset=utf-8")
w.Write([]byte(strings.ReplaceAll(out, `\n`, "\n")))
}

func (s *Server) RenderLandingPage(w http.ResponseWriter, r *http.Request) {
content, err := static.ReadFile("static/landing_page.html")
if err != nil {
http.Error(w, "static/landing_page.html not found", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write(content)
tmplLandingPage.Execute(w, nil)
}

func mustParseTemplate(filename string) *template.Template {
tmpl, err := template.ParseFS(static, "static/"+filename)
if err != nil {
panic("Template parsing failed: " + filename + " → " + err.Error())
}
return tmpl
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,63 +145,83 @@ func (*App) AdminSetRole(role string, addr std.Address) {
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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
)

func init() {
renderOpts := map[string]interface{}{
"Svg": struct{}{},
"Schedule": struct{}{},
}
renderOpts := map[string]interface{}{
"Svg": struct{}{},
"Schedule": struct{}{},
}
app.RegisterEvent(gnolandlaunch, renderOpts)
}

Expand Down Expand Up @@ -115,9 +115,9 @@ var gnolandlaunch = &event.Event{
// 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: []*session.Session{
// TODO: add finalized sessions here
// Sessions["keynote"],
Sessions["keynote"],
// Sessions["generics"],
// Sessions["concurrency"],
// Sessions["apis"],
Expand Down