From 3bec81d51c99922ce7a86376dc4070080f127133 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 13 Nov 2023 13:49:45 +0100 Subject: [PATCH 1/6] sort posts --- examples/gno.land/p/demo/blog/blog.gno | 11 ++++++++- examples/gno.land/p/demo/blog/util.gno | 34 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/blog/blog.gno b/examples/gno.land/p/demo/blog/blog.gno index 1cf37b7ad3a..78748c97d44 100644 --- a/examples/gno.land/p/demo/blog/blog.gno +++ b/examples/gno.land/p/demo/blog/blog.gno @@ -42,11 +42,20 @@ func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) { } res.Write("
") + + var sorted []*Post b.Posts.Iterate("", "", func(key string, value interface{}) bool { post := value.(*Post) - res.Write(post.RenderListItem()) + sorted = append(sorted, post) return false }) + + sorted = mergeSort(sorted) + + for _, post := range sorted { + res.Write(post.RenderListItem()) + } + res.Write("
") // FIXME: tag list/cloud. diff --git a/examples/gno.land/p/demo/blog/util.gno b/examples/gno.land/p/demo/blog/util.gno index b4a8652af72..bf62c7ff47c 100644 --- a/examples/gno.land/p/demo/blog/util.gno +++ b/examples/gno.land/p/demo/blog/util.gno @@ -5,3 +5,37 @@ import "strings" func breadcrumb(parts []string) string { return "# " + strings.Join(parts, " / ") + "\n\n" } + +func merge(left, right []*Post) []*Post { + result := make([]*Post, 0, len(left)+len(right)) + + for len(left) > 0 || len(right) > 0 { + if len(left) == 0 { + return append(result, right...) + } + if len(right) == 0 { + return append(result, left...) + } + if left[0].CreatedAt.After(right[0].CreatedAt) { + result = append(result, left[0]) + left = left[1:] + } else { + result = append(result, right[0]) + right = right[1:] + } + } + + return result +} + +func mergeSort(posts []*Post) []*Post { + if len(posts) <= 1 { + return posts + } + + middle := len(posts) / 2 + left := mergeSort(posts[:middle]) + right := mergeSort(posts[middle:]) + + return merge(left, right) +} From be2e95a7a79fc856906d540420c944cd210a4396 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 22 Nov 2023 11:48:59 +0100 Subject: [PATCH 2/6] refactor with slice --- examples/gno.land/p/demo/blog/blog.gno | 96 ++++++++----------------- examples/gno.land/p/demo/blog/types.gno | 36 ++++++++++ examples/gno.land/p/demo/blog/util.gno | 41 ++++------- 3 files changed, 80 insertions(+), 93 deletions(-) create mode 100644 examples/gno.land/p/demo/blog/types.gno diff --git a/examples/gno.land/p/demo/blog/blog.gno b/examples/gno.land/p/demo/blog/blog.gno index 78748c97d44..6773f1a97c0 100644 --- a/examples/gno.land/p/demo/blog/blog.gno +++ b/examples/gno.land/p/demo/blog/blog.gno @@ -7,27 +7,18 @@ import ( "strings" "time" - "gno.land/p/demo/avl" "gno.land/p/demo/mux" "gno.land/p/demo/ufmt" ) -type Blog struct { - Title string - Prefix string // i.e. r/gnoland/blog: - Posts avl.Tree // slug -> Post - NoBreadcrumb bool -} - func (b Blog) RenderLastPostsWidget(limit int) string { output := "" - i := 0 - b.Posts.Iterate("", "", func(key string, value interface{}) bool { - p := value.(*Post) + for i, p := range b.Posts { + if i >= limit { + break + } output += ufmt.Sprintf("- [%s](%s)\n", p.Title, p.URL()) - i++ - return i >= limit - }) + } return output } @@ -36,23 +27,14 @@ func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) { res.Write(breadcrumb([]string{b.Title})) } - if b.Posts.Size() == 0 { + if len(b.Posts) == 0 { res.Write("No posts.") return } res.Write("
") - var sorted []*Post - b.Posts.Iterate("", "", func(key string, value interface{}) bool { - post := value.(*Post) - sorted = append(sorted, post) - return false - }) - - sorted = mergeSort(sorted) - - for _, post := range sorted { + for _, post := range b.Posts { res.Write(post.RenderListItem()) } @@ -64,29 +46,30 @@ func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) { func (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) { slug := req.GetVar("slug") - post, found := b.Posts.Get(slug) - if !found { + post := b.Posts.GetPost(slug) + // TODO add search by slug + + if post == nil { res.Write("404") return } - p := post.(*Post) if !b.NoBreadcrumb { breadStr := breadcrumb([]string{ ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix), "p", - p.Title, + post.Title, }) res.Write(breadStr) } // output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL()) - res.Write(p.Body + "\n\n") - res.Write(p.RenderTagList() + "\n\n") - res.Write(formatAuthorAndDate(p.Author, p.CreatedAt) + "\n\n") + res.Write(post.Body + "\n\n") + res.Write(post.RenderTagList() + "\n\n") + res.Write(formatAuthorAndDate(post.Author, post.CreatedAt) + "\n\n") // comments - p.Comments.ReverseIterate("", "", func(key string, value interface{}) bool { + post.Comments.ReverseIterate("", "", func(key string, value interface{}) bool { comment := value.(*Comment) res.Write(comment.RenderListItem()) return false @@ -111,18 +94,17 @@ func (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) { } nb := 0 - b.Posts.Iterate("", "", func(key string, value interface{}) bool { - post := value.(*Post) - if !post.HasTag(slug) { - return false + for _, post := range b.Posts { + if post.HasTag(slug) { + res.Write(post.RenderListItem()) + nb++ } - res.Write(post.RenderListItem()) - nb++ - return false - }) + } + if nb == 0 { res.Write("No posts.") } + } func (b Blog) Render(path string) string { @@ -134,8 +116,8 @@ func (b Blog) Render(path string) string { } func (b *Blog) NewPost(author std.Address, slug, title, body string, tags []string) error { - _, found := b.Posts.Get(slug) - if found { + p := b.Posts.GetPost(slug) + if p != nil { return errors.New("slug already exists.") } @@ -168,29 +150,16 @@ func (b *Blog) prepareAndSetPost(post *Post) error { post.Blog = b post.UpdatedAt = time.Now() - b.Posts.Set(post.Slug, post) + b.Posts = append(b.Posts, post) return nil } func (b *Blog) GetPost(slug string) *Post { - post, found := b.Posts.Get(slug) - if !found { + post := b.Posts.GetPost(slug) + if post == nil { return nil } - return post.(*Post) -} - -type Post struct { - Blog *Blog - Slug string // FIXME: save space? - Title string - Body string - CreatedAt time.Time - UpdatedAt time.Time - Comments avl.Tree - Author std.Address - Tags []string - CommentIndex int + return post } func (p *Post) Update(title, body string, tags []string) error { @@ -288,13 +257,6 @@ func (p *Post) Summary() string { return strings.Join(lines[0:3], "\n") + "..." } -type Comment struct { - Post *Post - CreatedAt time.Time - Author std.Address - Comment string -} - func (c Comment) RenderListItem() string { output := "" output += ufmt.Sprintf("#### %s\n", formatAuthorAndDate(c.Author, c.CreatedAt)) diff --git a/examples/gno.land/p/demo/blog/types.gno b/examples/gno.land/p/demo/blog/types.gno new file mode 100644 index 00000000000..05c66197ed8 --- /dev/null +++ b/examples/gno.land/p/demo/blog/types.gno @@ -0,0 +1,36 @@ +package blog + +import ( + "gno.land/p/demo/avl" + "std" + "time" +) + +type Blog struct { + Title string + Prefix string // i.e. r/gnoland/blog: + Posts postSlice + NoBreadcrumb bool +} + +type Post struct { + Blog *Blog + Slug string + Title string + Body string + CreatedAt time.Time + UpdatedAt time.Time + Comments avl.Tree // TODO refactor to slice + Author std.Address + Tags []string + CommentIndex int +} + +type Comment struct { + Post *Post + CreatedAt time.Time + Author std.Address + Comment string +} + +type postSlice []*Post diff --git a/examples/gno.land/p/demo/blog/util.gno b/examples/gno.land/p/demo/blog/util.gno index bf62c7ff47c..10a5222ff89 100644 --- a/examples/gno.land/p/demo/blog/util.gno +++ b/examples/gno.land/p/demo/blog/util.gno @@ -6,36 +6,25 @@ func breadcrumb(parts []string) string { return "# " + strings.Join(parts, " / ") + "\n\n" } -func merge(left, right []*Post) []*Post { - result := make([]*Post, 0, len(left)+len(right)) - - for len(left) > 0 || len(right) > 0 { - if len(left) == 0 { - return append(result, right...) - } - if len(right) == 0 { - return append(result, left...) - } - if left[0].CreatedAt.After(right[0].CreatedAt) { - result = append(result, left[0]) - left = left[1:] - } else { - result = append(result, right[0]) - right = right[1:] +func (p postSlice) GetPost(slug string) *Post { + for _, post := range p { + if post.Slug == slug { + return post } } - - return result + return nil } -func mergeSort(posts []*Post) []*Post { - if len(posts) <= 1 { - return posts - } +// Sort interface implementation for postSlice + +func (p postSlice) Len() int { + return len(p) +} - middle := len(posts) / 2 - left := mergeSort(posts[:middle]) - right := mergeSort(posts[middle:]) +func (p postSlice) Less(i, j int) bool { + return p[i].CreatedAt.Before(p[j].CreatedAt) +} - return merge(left, right) +func (p postSlice) Swap(i, j int) { + p[i], p[j] = p[j], p[i] } From 670d659c8adb6acd540f5fd072cac0f69d3b4e31 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 22 Nov 2023 11:53:29 +0100 Subject: [PATCH 3/6] make fmt --- examples/gno.land/p/demo/blog/blog.gno | 1 - examples/gno.land/p/demo/blog/types.gno | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/blog/blog.gno b/examples/gno.land/p/demo/blog/blog.gno index 6773f1a97c0..ff2427612b0 100644 --- a/examples/gno.land/p/demo/blog/blog.gno +++ b/examples/gno.land/p/demo/blog/blog.gno @@ -104,7 +104,6 @@ func (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) { if nb == 0 { res.Write("No posts.") } - } func (b Blog) Render(path string) string { diff --git a/examples/gno.land/p/demo/blog/types.gno b/examples/gno.land/p/demo/blog/types.gno index 05c66197ed8..cc23cf09ccc 100644 --- a/examples/gno.land/p/demo/blog/types.gno +++ b/examples/gno.land/p/demo/blog/types.gno @@ -1,9 +1,10 @@ package blog import ( - "gno.land/p/demo/avl" "std" "time" + + "gno.land/p/demo/avl" ) type Blog struct { From 1251f6070dc9058d8d84206fada3279fefc32292 Mon Sep 17 00:00:00 2001 From: Leon Hudak <33522493+leohhhn@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:09:39 +0100 Subject: [PATCH 4/6] Update examples/gno.land/p/demo/blog/util.gno Co-authored-by: Morgan --- examples/gno.land/p/demo/blog/util.gno | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/gno.land/p/demo/blog/util.gno b/examples/gno.land/p/demo/blog/util.gno index 10a5222ff89..d567f53cd43 100644 --- a/examples/gno.land/p/demo/blog/util.gno +++ b/examples/gno.land/p/demo/blog/util.gno @@ -28,3 +28,5 @@ func (p postSlice) Less(i, j int) bool { func (p postSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +var _ sort.Interface = postSlice(nil) From 007f110e2620d88c6ff91f870cb3d39087003ba4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 27 Nov 2023 12:57:05 +0100 Subject: [PATCH 5/6] wip added modification for comments --- examples/gno.land/p/demo/blog/blog.gno | 37 ++++++++++-------------- examples/gno.land/p/demo/blog/errors.gno | 1 + examples/gno.land/p/demo/blog/gno.mod | 2 +- examples/gno.land/p/demo/blog/types.gno | 25 ++++++++-------- examples/gno.land/p/demo/blog/util.gno | 30 +++++++++++++++++-- 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/examples/gno.land/p/demo/blog/blog.gno b/examples/gno.land/p/demo/blog/blog.gno index ff2427612b0..ae1319f31e7 100644 --- a/examples/gno.land/p/demo/blog/blog.gno +++ b/examples/gno.land/p/demo/blog/blog.gno @@ -3,7 +3,6 @@ package blog import ( "errors" "std" - "strconv" "strings" "time" @@ -46,8 +45,7 @@ func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) { func (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) { slug := req.GetVar("slug") - post := b.Posts.GetPost(slug) - // TODO add search by slug + post := b.Posts.GetPostBySlug(slug) if post == nil { res.Write("404") @@ -69,11 +67,9 @@ func (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) { res.Write(formatAuthorAndDate(post.Author, post.CreatedAt) + "\n\n") // comments - post.Comments.ReverseIterate("", "", func(key string, value interface{}) bool { - comment := value.(*Comment) + for _, comment := range post.Comments { res.Write(comment.RenderListItem()) - return false - }) + } } func (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) { @@ -115,7 +111,7 @@ func (b Blog) Render(path string) string { } func (b *Blog) NewPost(author std.Address, slug, title, body string, tags []string) error { - p := b.Posts.GetPost(slug) + p := b.Posts.GetPostBySlug(slug) if p != nil { return errors.New("slug already exists.") } @@ -153,14 +149,6 @@ func (b *Blog) prepareAndSetPost(post *Post) error { return nil } -func (b *Blog) GetPost(slug string) *Post { - post := b.Posts.GetPost(slug) - if post == nil { - return nil - } - return post -} - func (p *Post) Update(title, body string, tags []string) error { p.Title = title p.Body = body @@ -172,10 +160,10 @@ func (p *Post) AddComment(author std.Address, comment string) error { if p == nil { return ErrNoSuchPost } - p.CommentIndex++ - commentKey := strconv.Itoa(p.CommentIndex) comment = strings.TrimSpace(comment) - p.Comments.Set(commentKey, &Comment{ + + p.Comments = append(p.Comments, &Comment{ + ID: p.commentID.Next(), Post: p, CreatedAt: time.Now(), Author: author, @@ -185,12 +173,17 @@ func (p *Post) AddComment(author std.Address, comment string) error { return nil } -func (p *Post) DeleteComment(index int) error { +func (p *Post) DeleteComment(id int) error { if p == nil { return ErrNoSuchPost } - commentKey := strconv.Itoa(index) - p.Comments.Remove(commentKey) + + updatedSlice, err := p.Comments.RemoveByID(id) + if err != nil { + return err + } + + p.Comments = updatedSlice return nil } diff --git a/examples/gno.land/p/demo/blog/errors.gno b/examples/gno.land/p/demo/blog/errors.gno index db9f8f39fa1..82447b02134 100644 --- a/examples/gno.land/p/demo/blog/errors.gno +++ b/examples/gno.land/p/demo/blog/errors.gno @@ -3,3 +3,4 @@ package blog import "errors" var ErrNoSuchPost = errors.New("no such post") +var ErrNoSuchComment = errors.New("no such comment") diff --git a/examples/gno.land/p/demo/blog/gno.mod b/examples/gno.land/p/demo/blog/gno.mod index 65f58e7a0f6..952ee61f715 100644 --- a/examples/gno.land/p/demo/blog/gno.mod +++ b/examples/gno.land/p/demo/blog/gno.mod @@ -1,7 +1,7 @@ module gno.land/p/demo/blog require ( - gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/mux v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/blog/types.gno b/examples/gno.land/p/demo/blog/types.gno index cc23cf09ccc..9daf67ea3b7 100644 --- a/examples/gno.land/p/demo/blog/types.gno +++ b/examples/gno.land/p/demo/blog/types.gno @@ -1,10 +1,9 @@ package blog import ( + "gno.land/p/demo/seqid" "std" "time" - - "gno.land/p/demo/avl" ) type Blog struct { @@ -15,19 +14,20 @@ type Blog struct { } type Post struct { - Blog *Blog - Slug string - Title string - Body string - CreatedAt time.Time - UpdatedAt time.Time - Comments avl.Tree // TODO refactor to slice - Author std.Address - Tags []string - CommentIndex int + Blog *Blog + Slug string + Title string + Body string + CreatedAt time.Time + UpdatedAt time.Time + Comments commentSlice + Author std.Address + Tags []string + commentID seqid.ID } type Comment struct { + ID int Post *Post CreatedAt time.Time Author std.Address @@ -35,3 +35,4 @@ type Comment struct { } type postSlice []*Post +type commentSlice []*Comment diff --git a/examples/gno.land/p/demo/blog/util.gno b/examples/gno.land/p/demo/blog/util.gno index d567f53cd43..507a01c88d1 100644 --- a/examples/gno.land/p/demo/blog/util.gno +++ b/examples/gno.land/p/demo/blog/util.gno @@ -1,12 +1,14 @@ package blog -import "strings" +import ( + "strings" +) func breadcrumb(parts []string) string { return "# " + strings.Join(parts, " / ") + "\n\n" } -func (p postSlice) GetPost(slug string) *Post { +func (p postSlice) GetPostBySlug(slug string) *Post { for _, post := range p { if post.Slug == slug { return post @@ -15,7 +17,16 @@ func (p postSlice) GetPost(slug string) *Post { return nil } -// Sort interface implementation for postSlice +func (c commentSlice) RemoveByID(id int) (commentSlice, error) { + for i, comment := range c { + if comment.ID == id { + return append(c[:i], c[i+1:]...), nil + } + } + return nil, ErrNoSuchComment +} + +// Sort interface implementations for postSlice & commentSlice func (p postSlice) Len() int { return len(p) @@ -29,4 +40,17 @@ func (p postSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (c commentSlice) Len() int { + return len(c) +} + +func (c commentSlice) Less(i, j int) bool { + return c[i].CreatedAt.Before(c[j].CreatedAt) +} + +func (c commentSlice) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + var _ sort.Interface = postSlice(nil) +var _ sort.Interface = commentSlice(nil) From 22305c3fabf971db5a0954b249895b4b9904e6f7 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 27 Nov 2023 18:43:51 +0100 Subject: [PATCH 6/6] updated blog post fetching api in different realms --- examples/gno.land/p/demo/blog/types.gno | 2 +- examples/gno.land/p/demo/blog/util.gno | 1 + examples/gno.land/r/gnoland/blog/admin.gno | 4 ++-- examples/gno.land/r/gnoland/blog/gnoblog.gno | 2 +- examples/gno.land/r/gnoland/pages/admin.gno | 2 +- examples/gno.land/r/manfred/present/admin.gno | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/p/demo/blog/types.gno b/examples/gno.land/p/demo/blog/types.gno index 9daf67ea3b7..b13ccef593d 100644 --- a/examples/gno.land/p/demo/blog/types.gno +++ b/examples/gno.land/p/demo/blog/types.gno @@ -27,7 +27,7 @@ type Post struct { } type Comment struct { - ID int + ID seqid.ID Post *Post CreatedAt time.Time Author std.Address diff --git a/examples/gno.land/p/demo/blog/util.gno b/examples/gno.land/p/demo/blog/util.gno index 507a01c88d1..a5d9ba2d72e 100644 --- a/examples/gno.land/p/demo/blog/util.gno +++ b/examples/gno.land/p/demo/blog/util.gno @@ -1,6 +1,7 @@ package blog import ( + "sort" "strings" ) diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 646fe5f155e..187353337f8 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -52,7 +52,7 @@ func ModEditPost(slug, title, body, tags string) { assertIsModerator() tagList := strings.Split(tags, ",") - err := b.GetPost(slug).Update(title, body, tagList) + err := b.Posts.GetPostBySlug(slug).Update(title, body, tagList) checkErr(err) } @@ -69,7 +69,7 @@ func ModDelCommenter(addr std.Address) { func ModDelComment(slug string, index int) { assertIsModerator() - err := b.GetPost(slug).DeleteComment(index) + err := b.Posts.GetPostBySlug(slug).DeleteComment(index) checkErr(err) } diff --git a/examples/gno.land/r/gnoland/blog/gnoblog.gno b/examples/gno.land/r/gnoland/blog/gnoblog.gno index cad84507614..827728557f0 100644 --- a/examples/gno.land/r/gnoland/blog/gnoblog.gno +++ b/examples/gno.land/r/gnoland/blog/gnoblog.gno @@ -16,7 +16,7 @@ func AddComment(postSlug, comment string) { assertNotInPause() caller := std.GetOrigCaller() - err := b.GetPost(postSlug).AddComment(caller, comment) + err := b.Posts.GetPostBySlug(postSlug).AddComment(caller, comment) checkErr(err) } diff --git a/examples/gno.land/r/gnoland/pages/admin.gno b/examples/gno.land/r/gnoland/pages/admin.gno index 39fba6d3274..118afb0b671 100644 --- a/examples/gno.land/r/gnoland/pages/admin.gno +++ b/examples/gno.land/r/gnoland/pages/admin.gno @@ -51,7 +51,7 @@ func ModEditPost(slug, title, body, tags string) { assertIsModerator() tagList := strings.Split(tags, ",") - err := b.GetPost(slug).Update(title, body, tagList) + err := b.Posts.GetPostBySlug(slug).Update(title, body, tagList) checkErr(err) } diff --git a/examples/gno.land/r/manfred/present/admin.gno b/examples/gno.land/r/manfred/present/admin.gno index ff0cb075656..d3ffb34f582 100644 --- a/examples/gno.land/r/manfred/present/admin.gno +++ b/examples/gno.land/r/manfred/present/admin.gno @@ -51,7 +51,7 @@ func ModEditPost(slug, title, body, tags string) { assertIsModerator() tagList := strings.Split(tags, ",") - err := b.GetPost(slug).Update(title, body, tagList) + err := b.Posts.GetPostBySlug(slug).Update(title, body, tagList) checkErr(err) }