Skip to content

Commit

Permalink
Merge branch 'master' into bug/nil-typeassert2
Browse files Browse the repository at this point in the history
  • Loading branch information
deelawn authored Feb 27, 2024
2 parents 2695b64 + 6f4efc6 commit 7d4e8e6
Show file tree
Hide file tree
Showing 19 changed files with 253 additions and 157 deletions.
162 changes: 117 additions & 45 deletions examples/gno.land/p/demo/blog/blog.gno
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package blog

import (
"errors"
"std"
"strconv"
"strings"
Expand All @@ -13,10 +12,12 @@ import (
)

type Blog struct {
Title string
Prefix string // i.e. r/gnoland/blog:
Posts avl.Tree // slug -> Post
NoBreadcrumb bool
Title string
Prefix string // i.e. r/gnoland/blog:
Posts avl.Tree // slug -> *Post
PostsPublished avl.Tree // published-date -> *Post
PostsAlphabetical avl.Tree // title -> *Post
NoBreadcrumb bool
}

func (b Blog) RenderLastPostsWidget(limit int) string {
Expand All @@ -42,7 +43,7 @@ func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) {
}

res.Write("<div class='columns-3'>")
b.Posts.Iterate("", "", func(key string, value interface{}) bool {
b.PostsPublished.ReverseIterate("", "", func(key string, value interface{}) bool {
post := value.(*Post)
res.Write(post.RenderListItem())
return false
Expand All @@ -62,26 +63,25 @@ func (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) {
}
p := post.(*Post)

if !b.NoBreadcrumb {
breadStr := breadcrumb([]string{
ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix),
"p",
p.Title,
})
res.Write(breadStr)
}

// output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL())
res.Write("# " + p.Title + "\n\n")
res.Write(p.Body + "\n\n")
res.Write("---\n\n")

res.Write(p.RenderTagList() + "\n\n")
res.Write(formatAuthorAndDate(p.Author, p.CreatedAt) + "\n\n")
res.Write(p.RenderAuthorList() + "\n\n")
res.Write(p.RenderPublishData() + "\n\n")

res.Write("---\n")
res.Write("<details><summary>Comment section</summary>\n\n")

// comments
p.Comments.ReverseIterate("", "", func(key string, value interface{}) bool {
comment := value.(*Comment)
res.Write(comment.RenderListItem())
return false
})

res.Write("</details>\n")
}

func (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) {
Expand Down Expand Up @@ -124,42 +124,70 @@ func (b Blog) Render(path string) string {
return router.Render(path)
}

func (b *Blog) NewPost(author std.Address, slug, title, body string, tags []string) error {
_, found := b.Posts.Get(slug)
if found {
return errors.New("slug already exists.")
func (b *Blog) NewPost(publisher std.Address, slug, title, body, pubDate string, authors, tags []string) error {
if _, found := b.Posts.Get(slug); found {
return ErrPostSlugExists
}

post := Post{
Author: author,
var parsedTime time.Time
var err error
if pubDate != "" {
parsedTime, err = time.Parse(time.RFC3339, pubDate)
if err != nil {
return err
}
} else {
// If no publication date was passed in by caller, take current block time
parsedTime = time.Now()
}

post := &Post{
Publisher: publisher,
Authors: authors,
Slug: slug,
Title: title,
Body: body,
Tags: tags,
CreatedAt: time.Now(),
CreatedAt: parsedTime,
}
return b.prepareAndSetPost(&post)

return b.prepareAndSetPost(post)
}

func (b *Blog) prepareAndSetPost(post *Post) error {
post.Title = strings.TrimSpace(post.Title)
post.Body = strings.TrimSpace(post.Body)

if post.Title == "" {
return errors.New("title is missing.")
return ErrPostTitleMissing
}
if post.Body == "" {
return errors.New("body is missing.")
return ErrPostBodyMissing
}
if post.Slug == "" {
return errors.New("slug is missing.")
return ErrPostSlugMissing
}
// more input sanitization?

post.Blog = b
post.UpdatedAt = time.Now()

trimmedTitleKey := strings.Replace(post.Title, " ", "", -1)
pubDateKey := post.CreatedAt.Format(time.RFC3339)

// Cannot have two posts with same title key
if _, found := b.PostsAlphabetical.Get(trimmedTitleKey); found {
return ErrPostTitleExists
}
// Cannot have two posts with *exact* same timestamp
if _, found := b.PostsPublished.Get(pubDateKey); found {
return ErrPostPubDateExists
}

// Store post under keys
b.PostsAlphabetical.Set(trimmedTitleKey, post)
b.PostsPublished.Set(pubDateKey, post)
b.Posts.Set(post.Slug, post)

return nil
}

Expand All @@ -179,15 +207,24 @@ type Post struct {
CreatedAt time.Time
UpdatedAt time.Time
Comments avl.Tree
Author std.Address
Authors []string
Publisher std.Address
Tags []string
CommentIndex int
}

func (p *Post) Update(title, body string, tags []string) error {
func (p *Post) Update(title, body, publicationDate string, authors, tags []string) error {
p.Title = title
p.Body = body
p.Tags = tags
p.Authors = authors

parsedTime, err := time.Parse(time.RFC3339, publicationDate)
if err != nil {
return err
}

p.CreatedAt = parsedTime
return p.Blog.prepareAndSetPost(p)
}

Expand Down Expand Up @@ -234,31 +271,66 @@ func (p *Post) RenderListItem() string {
return "error: no such post\n"
}
output := "<div>\n\n"
output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL())
output += ufmt.Sprintf("**[Learn More](%s)**\n", p.URL())
output += ufmt.Sprintf("### [%s](%s)\n", p.Title, p.URL())
// output += ufmt.Sprintf("**[Learn More](%s)**\n\n", p.URL())

output += " " + p.CreatedAt.Format("02 Jan 2006")
// output += p.Summary() + "\n\n"
// output += p.RenderTagList() + "\n\n"
// output += formatAuthorAndDate(p.Author, p.CreatedAt) + "\n"
output += "\n"
output += "</div>"
return output
}

// Render post tags
func (p *Post) RenderTagList() string {
if p == nil {
return "error: no such post\n"
}
output := ""
if len(p.Tags) == 0 {
return ""
}

output := "Tags: "
for idx, tag := range p.Tags {
if idx > 0 {
output += " "
}
tagURL := p.Blog.Prefix + "t/" + tag
output += ufmt.Sprintf("[#%s](%s)", tag, tagURL)

}
return output
}

// Render authors if there are any
func (p *Post) RenderAuthorList() string {
out := "Written"
if len(p.Authors) != 0 {
out += " by "

for idx, author := range p.Authors {
out += author
if idx < len(p.Authors)-1 {
out += ", "
}
}
}
out += " on " + p.CreatedAt.Format("02 Jan 2006")

return out
}

func (p *Post) RenderPublishData() string {
out := "Published "
if p.Publisher != "" {
out += "by " + p.Publisher.String() + " "
}
out += "to " + p.Blog.Title

return out
}

func (p *Post) URL() string {
if p == nil {
return p.Blog.Prefix + "404"
Expand Down Expand Up @@ -287,15 +359,15 @@ type Comment struct {
}

func (c Comment) RenderListItem() string {
output := ""
output += ufmt.Sprintf("#### %s\n", formatAuthorAndDate(c.Author, c.CreatedAt))
output += c.Comment + "\n"
output += "\n"
return output
}
output := "<h5>"
output += c.Comment + "\n\n"
output += "</h5>"

output += "<h6>"
output += ufmt.Sprintf("by %s on %s", c.Author, c.CreatedAt.Format(time.RFC822))
output += "</h6>\n\n"

func formatAuthorAndDate(author std.Address, createdAt time.Time) string {
authorString := author.String() // FIXME: username.
createdAtString := createdAt.Format("2006-01-02 3:04pm MST")
return ufmt.Sprintf("by %s on %s", authorString, createdAtString)
output += "---\n\n"

return output
}
10 changes: 9 additions & 1 deletion examples/gno.land/p/demo/blog/errors.gno
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@ package blog

import "errors"

var ErrNoSuchPost = errors.New("no such post")
var (
ErrPostTitleMissing = errors.New("post title is missing")
ErrPostSlugMissing = errors.New("post slug is missing")
ErrPostBodyMissing = errors.New("post body is missing")
ErrPostSlugExists = errors.New("post with specified slug already exists")
ErrPostPubDateExists = errors.New("post with specified publication date exists")
ErrPostTitleExists = errors.New("post with specified title already exists")
ErrNoSuchPost = errors.New("no such post")
)
22 changes: 17 additions & 5 deletions examples/gno.land/r/gnoland/blog/admin.gno
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,32 @@ func AdminRemoveModerator(addr std.Address) {
moderatorList.Set(addr.String(), false) // FIXME: delete instead?
}

func ModAddPost(slug, title, body, tags string) {
func ModAddPost(slug, title, body, publicationDate, authors, tags string) {
assertIsModerator()

caller := std.GetOrigCaller()
tagList := strings.Split(tags, ",")
err := b.NewPost(caller, slug, title, body, tagList)

var tagList []string
if tags != "" {
tagList = strings.Split(tags, ",")
}
var authorList []string
if authors != "" {
authorList = strings.Split(authors, ",")
}

err := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)

checkErr(err)
}

func ModEditPost(slug, title, body, tags string) {
func ModEditPost(slug, title, body, publicationDate, authors, tags string) {
assertIsModerator()

tagList := strings.Split(tags, ",")
err := b.GetPost(slug).Update(title, body, tagList)
authorList := strings.Split(authors, ",")

err := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)
checkErr(err)
}

Expand Down
7 changes: 7 additions & 0 deletions examples/gno.land/r/gnoland/blog/gnoblog.gno
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,10 @@ func Render(path string) string {
func RenderLastPostsWidget(limit int) string {
return b.RenderLastPostsWidget(limit)
}

func PostExists(slug string) bool {
if b.GetPost(slug) == nil {
return false
}
return true
}
Loading

0 comments on commit 7d4e8e6

Please sign in to comment.