From ac0d79ed64c7430c69c05a17851370559026aec9 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 07:59:03 +0200 Subject: [PATCH 01/71] feat: add p/avl/pager Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/pagination/gno.mod | 3 + .../gno.land/p/demo/pagination/pagination.gno | 135 ++++++++++++++++++ .../p/demo/pagination/pagination_test.gno | 112 +++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 examples/gno.land/p/demo/pagination/gno.mod create mode 100644 examples/gno.land/p/demo/pagination/pagination.gno create mode 100644 examples/gno.land/p/demo/pagination/pagination_test.gno diff --git a/examples/gno.land/p/demo/pagination/gno.mod b/examples/gno.land/p/demo/pagination/gno.mod new file mode 100644 index 00000000000..cf3f7587312 --- /dev/null +++ b/examples/gno.land/p/demo/pagination/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/pagination + +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/p/demo/pagination/pagination.gno b/examples/gno.land/p/demo/pagination/pagination.gno new file mode 100644 index 00000000000..30851e9eb20 --- /dev/null +++ b/examples/gno.land/p/demo/pagination/pagination.gno @@ -0,0 +1,135 @@ +package pagination + +import ( + "math" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +// Paginator is a struct that holds the AVL tree and pagination parameters. +type Paginator struct { + Tree *avl.Tree +} + +// Page represents a single page of results. +type Page struct { + Items []Item + PageNumber int + PageSize int + TotalItems int + TotalPages int + HasPrev bool + HasNext bool +} + +// Item represents a key-value pair in the AVL tree. +type Item struct { + Key string + Value interface{} +} + +// GetPage retrieves a page of results from the AVL tree. +func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { + if pageNumber < 1 || pageSize < 1 { + return nil, ufmt.Errorf("invalid page number or page size") + } + + totalItems := p.Tree.Size() + totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + + if pageNumber > totalPages { + return &Page{ + Items: []Item{}, + PageNumber: pageNumber, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: pageNumber > 1, + HasNext: pageNumber < totalPages, + }, nil + } + + startIndex := (pageNumber - 1) * pageSize + endIndex := startIndex + pageSize + if endIndex > totalItems { + endIndex = totalItems + } + + items := []Item{} + p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + items = append(items, Item{Key: key, Value: value}) + return false + }) + + return &Page{ + Items: items, + PageNumber: pageNumber, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: pageNumber > 1, + HasNext: pageNumber < totalPages, + }, nil +} + +// RenderPageSelector generates the Markdown for the page selector. +func (p *Paginator) RenderPageSelector(page *Page) string { + if page.TotalPages <= 1 { + return "" + } + + md := "" + + // First and Previous links + if page.HasPrev { + md += ufmt.Sprintf("[First](?page=1) | ") + md += ufmt.Sprintf("[Previous](?page=%d) | ", page.PageNumber-1) + } else { + md += "First | Previous | " + } + + // Page number links + startPage := max(1, page.PageNumber-2) + endPage := min(page.TotalPages, page.PageNumber+2) + + if startPage > 1 { + md += "… | " + } + + for i := startPage; i <= endPage; i++ { + if i == page.PageNumber { + md += ufmt.Sprintf("**%d** | ", i) + } else { + md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) + } + } + + if endPage < page.TotalPages { + md += "… | " + } + + // Next and Last links + if page.HasNext { + md += ufmt.Sprintf("[Next](?page=%d) | ", page.PageNumber+1) + md += ufmt.Sprintf("[Last](?page=%d)", page.TotalPages) + } else { + md += "Next | Last" + } + + return md +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/examples/gno.land/p/demo/pagination/pagination_test.gno b/examples/gno.land/p/demo/pagination/pagination_test.gno new file mode 100644 index 00000000000..faef4702f8a --- /dev/null +++ b/examples/gno.land/p/demo/pagination/pagination_test.gno @@ -0,0 +1,112 @@ +package pagination + +import ( + "testing" + + "gno.land/p/demo/avl" + "gno.land/p/demo/uassert" + "gno.land/p/demo/ufmt" + "gno.land/p/demo/urequire" +) + +func TestPaginator_GetPage(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases. + tests := []struct { + pageNumber int + pageSize int + expected []Item + }{ + {1, 2, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}}}, + {2, 2, []Item{{Key: "c", Value: 3}, {Key: "d", Value: 4}}}, + {3, 2, []Item{{Key: "e", Value: 5}}}, + {1, 3, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}}}, + {2, 3, []Item{{Key: "d", Value: 4}, {Key: "e", Value: 5}}}, + {1, 5, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}, {Key: "d", Value: 4}, {Key: "e", Value: 5}}}, + {2, 5, []Item{}}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) + + for i, item := range page.Items { + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + } + } +} + +func TestPaginator_RenderPageSelector(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases. + tests := []struct { + pageNumber int + pageSize int + expected string + }{ + {1, 2, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, + {2, 2, "[First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, + {3, 2, "[First](?page=1) | [Previous](?page=2) | … | [2](?page=2) | **3** | Next | Last"}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + md := paginator.RenderPageSelector(page) + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + } +} + +func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { + // Create a new AVL tree and populate it with many key-value pairs. + tree := avl.NewTree() + for i := 0; i < 100; i++ { + tree.Set(ufmt.Sprintf("key%d", i), i) + } + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases for a large number of pages. + tests := []struct { + pageNumber int + pageSize int + expected string + }{ + {1, 10, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, + {5, 10, "[First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, + {10, 10, "[First](?page=1) | [Previous](?page=9) | … | [8](?page=8) | [9](?page=9) | **10** | Next | Last"}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + md := paginator.RenderPageSelector(page) + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + } +} From a17edb25399b329303d552e756616ac225ff6749 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 08:31:52 +0200 Subject: [PATCH 02/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/gno.mod | 8 +++ .../pagination.gno => avl/pager/pager.gno} | 54 +++++++++++-------- .../pager/pager_test.gno} | 38 ++++++------- examples/gno.land/p/demo/pagination/gno.mod | 3 -- 4 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 examples/gno.land/p/demo/avl/pager/gno.mod rename examples/gno.land/p/demo/{pagination/pagination.gno => avl/pager/pager.gno} (62%) rename examples/gno.land/p/demo/{pagination/pagination_test.gno => avl/pager/pager_test.gno} (75%) delete mode 100644 examples/gno.land/p/demo/pagination/gno.mod diff --git a/examples/gno.land/p/demo/avl/pager/gno.mod b/examples/gno.land/p/demo/avl/pager/gno.mod new file mode 100644 index 00000000000..59c961d73f2 --- /dev/null +++ b/examples/gno.land/p/demo/avl/pager/gno.mod @@ -0,0 +1,8 @@ +module gno.land/p/demo/avl/pager + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/urequire v0.0.0-latest +) diff --git a/examples/gno.land/p/demo/pagination/pagination.gno b/examples/gno.land/p/demo/avl/pager/pager.gno similarity index 62% rename from examples/gno.land/p/demo/pagination/pagination.gno rename to examples/gno.land/p/demo/avl/pager/pager.gno index 30851e9eb20..29033fb46c1 100644 --- a/examples/gno.land/p/demo/pagination/pagination.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -1,4 +1,4 @@ -package pagination +package pager import ( "math" @@ -7,8 +7,8 @@ import ( "gno.land/p/demo/ufmt" ) -// Paginator is a struct that holds the AVL tree and pagination parameters. -type Paginator struct { +// Pager is a struct that holds the AVL tree and pagination parameters. +type Pager struct { Tree *avl.Tree } @@ -30,14 +30,26 @@ type Item struct { } // GetPage retrieves a page of results from the AVL tree. -func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { - if pageNumber < 1 || pageSize < 1 { - return nil, ufmt.Errorf("invalid page number or page size") +func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { + if pageSize < 1 { + return nil, ufmt.Errorf("invalid page size") } totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + if pageNumber < 1 { + return &Page{ + Items: []Item{}, + PageNumber: 0, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: false, + HasNext: pageNumber < totalPages, + }, nil + } + if pageNumber > totalPages { return &Page{ Items: []Item{}, @@ -45,8 +57,8 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { PageSize: pageSize, TotalItems: totalItems, TotalPages: totalPages, - HasPrev: pageNumber > 1, - HasNext: pageNumber < totalPages, + HasPrev: totalPages > 0, + HasNext: false, }, nil } @@ -57,7 +69,7 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { } items := []Item{} - p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { items = append(items, Item{Key: key, Value: value}) return false }) @@ -73,46 +85,46 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { }, nil } -// RenderPageSelector generates the Markdown for the page selector. -func (p *Paginator) RenderPageSelector(page *Page) string { - if page.TotalPages <= 1 { +// RenderSelector generates the Markdown for the page selector. +func (p *Page) RenderSelector() string { + if p.TotalPages <= 1 { return "" } md := "" // First and Previous links - if page.HasPrev { + if p.HasPrev { md += ufmt.Sprintf("[First](?page=1) | ") - md += ufmt.Sprintf("[Previous](?page=%d) | ", page.PageNumber-1) + md += ufmt.Sprintf("[Previous](?page=%d) | ", p.PageNumber-1) } else { md += "First | Previous | " } // Page number links - startPage := max(1, page.PageNumber-2) - endPage := min(page.TotalPages, page.PageNumber+2) + startPage := max(1, p.PageNumber-2) + endPage := min(p.TotalPages, p.PageNumber+2) if startPage > 1 { md += "… | " } for i := startPage; i <= endPage; i++ { - if i == page.PageNumber { + if i == p.PageNumber { md += ufmt.Sprintf("**%d** | ", i) } else { md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) } } - if endPage < page.TotalPages { + if endPage < p.TotalPages { md += "… | " } // Next and Last links - if page.HasNext { - md += ufmt.Sprintf("[Next](?page=%d) | ", page.PageNumber+1) - md += ufmt.Sprintf("[Last](?page=%d)", page.TotalPages) + if p.HasNext { + md += ufmt.Sprintf("[Next](?page=%d) | ", p.PageNumber+1) + md += ufmt.Sprintf("[Last](?page=%d)", p.TotalPages) } else { md += "Next | Last" } diff --git a/examples/gno.land/p/demo/pagination/pagination_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno similarity index 75% rename from examples/gno.land/p/demo/pagination/pagination_test.gno rename to examples/gno.land/p/demo/avl/pager/pager_test.gno index faef4702f8a..ac8d0add82f 100644 --- a/examples/gno.land/p/demo/pagination/pagination_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -1,4 +1,4 @@ -package pagination +package pager import ( "testing" @@ -9,7 +9,7 @@ import ( "gno.land/p/demo/urequire" ) -func TestPaginator_GetPage(t *testing.T) { +func TestPager_GetPage(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -18,8 +18,8 @@ func TestPaginator_GetPage(t *testing.T) { tree.Set("d", 4) tree.Set("e", 5) - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases. tests := []struct { @@ -37,19 +37,19 @@ func TestPaginator_GetPage(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Key, tt.expected[i].Key)) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Value, tt.expected[i].Value)) } } } -func TestPaginator_RenderPageSelector(t *testing.T) { +func TestPage_RenderSelector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -58,8 +58,8 @@ func TestPaginator_RenderPageSelector(t *testing.T) { tree.Set("d", 4) tree.Set("e", 5) - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases. tests := []struct { @@ -73,23 +73,23 @@ func TestPaginator_RenderPageSelector(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := paginator.RenderPageSelector(page) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + md := page.RenderSelector() + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } -func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { +func TestPage_RenderSelector_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { tree.Set(ufmt.Sprintf("key%d", i), i) } - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases for a large number of pages. tests := []struct { @@ -103,10 +103,10 @@ func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := paginator.RenderPageSelector(page) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + md := page.RenderSelector() + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } diff --git a/examples/gno.land/p/demo/pagination/gno.mod b/examples/gno.land/p/demo/pagination/gno.mod deleted file mode 100644 index cf3f7587312..00000000000 --- a/examples/gno.land/p/demo/pagination/gno.mod +++ /dev/null @@ -1,3 +0,0 @@ -module gno.land/p/demo/pagination - -require gno.land/p/demo/avl v0.0.0-latest From 1a44f35682c9b7866020b0f42ecdcf90f0c2ed67 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 08:31:56 +0200 Subject: [PATCH 03/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/z_filetest.gno | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 examples/gno.land/p/demo/avl/pager/z_filetest.gno diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno new file mode 100644 index 00000000000..43437d5f27b --- /dev/null +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -0,0 +1,173 @@ +package main + +import ( + "fmt" + + "gno.land/p/demo/avl" + "gno.land/p/demo/avl/pager" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) + +func main() { + // Create a new AVL tree and populate it with some key-value pairs. + var id seqid.ID + tree := avl.NewTree() + for i := 0; i < 42; i++ { + // tree.Set(fmt.Sprintf("key%d", i), i) + tree.Set(id.Next().String(), i) + } + + // Create a new pager. + pager := pager.Pager{Tree: tree} + + for pn := -1; pn < 8; pn++ { + page, err := pager.GetPage(pn, 7) + if err != nil { + println("Error:", err.Error()) + return + } + + // Print the items on the first page. + println(ufmt.Sprintf("Page %d of %d\n", page.PageNumber, page.TotalPages)) + for _, item := range page.Items { + println(fmt.Sprintf("Key: %s, Value: %v\n", item.Key, item.Value)) + } + + // Render and print the page selector. + pageSelector := page.RenderSelector() + println("Page Selector:") + println(pageSelector) + println() + } +} + +// Output: +// Page 0 of 6 +// +// Page Selector: +// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// +// Page 0 of 6 +// +// Page Selector: +// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// +// Page 1 of 6 +// +// Key: 0000001, Value: 0 +// +// Key: 0000002, Value: 1 +// +// Key: 0000003, Value: 2 +// +// Key: 0000004, Value: 3 +// +// Key: 0000005, Value: 4 +// +// Key: 0000006, Value: 5 +// +// Key: 0000007, Value: 6 +// +// Page Selector: +// First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=6) +// +// Page 2 of 6 +// +// Key: 0000008, Value: 7 +// +// Key: 0000009, Value: 8 +// +// Key: 000000a, Value: 9 +// +// Key: 000000b, Value: 10 +// +// Key: 000000c, Value: 11 +// +// Key: 000000d, Value: 12 +// +// Key: 000000e, Value: 13 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [Next](?page=3) | [Last](?page=6) +// +// Page 3 of 6 +// +// Key: 000000f, Value: 14 +// +// Key: 000000g, Value: 15 +// +// Key: 000000h, Value: 16 +// +// Key: 000000j, Value: 17 +// +// Key: 000000k, Value: 18 +// +// Key: 000000m, Value: 19 +// +// Key: 000000n, Value: 20 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=2) | [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [Next](?page=4) | [Last](?page=6) +// +// Page 4 of 6 +// +// Key: 000000p, Value: 21 +// +// Key: 000000q, Value: 22 +// +// Key: 000000r, Value: 23 +// +// Key: 000000s, Value: 24 +// +// Key: 000000t, Value: 25 +// +// Key: 000000v, Value: 26 +// +// Key: 000000w, Value: 27 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=3) | … | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | [Next](?page=5) | [Last](?page=6) +// +// Page 5 of 6 +// +// Key: 000000x, Value: 28 +// +// Key: 000000y, Value: 29 +// +// Key: 000000z, Value: 30 +// +// Key: 0000010, Value: 31 +// +// Key: 0000011, Value: 32 +// +// Key: 0000012, Value: 33 +// +// Key: 0000013, Value: 34 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [Next](?page=6) | [Last](?page=6) +// +// Page 6 of 6 +// +// Key: 0000014, Value: 35 +// +// Key: 0000015, Value: 36 +// +// Key: 0000016, Value: 37 +// +// Key: 0000017, Value: 38 +// +// Key: 0000018, Value: 39 +// +// Key: 0000019, Value: 40 +// +// Key: 000001a, Value: 41 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=5) | … | [4](?page=4) | [5](?page=5) | **6** | Next | Last +// +// Page 7 of 6 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=6) | … | [5](?page=5) | [6](?page=6) | Next | Last From 291b92fd128b845a984beb085da32a44d8addc30 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 09:02:40 +0200 Subject: [PATCH 04/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 84 ++++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 68 +++++-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 166 ++++++------------ 3 files changed, 168 insertions(+), 150 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 29033fb46c1..ad5234254bf 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -2,6 +2,8 @@ package pager import ( "math" + "net/url" + "strconv" "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" @@ -9,7 +11,9 @@ import ( // Pager is a struct that holds the AVL tree and pagination parameters. type Pager struct { - Tree *avl.Tree + Tree *avl.Tree + PageQueryParam string + DefaultPageSize int } // Page represents a single page of results. @@ -29,6 +33,15 @@ type Item struct { Value interface{} } +// NewPager creates a new Pager with default values. +func NewPager(tree *avl.Tree) *Pager { + return &Pager{ + Tree: tree, + PageQueryParam: "page", + DefaultPageSize: 10, + } +} + // GetPage retrieves a page of results from the AVL tree. func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { if pageSize < 1 { @@ -86,52 +99,71 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { } // RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector() string { +func (p *Page) RenderSelector(queryParam string) string { if p.TotalPages <= 1 { return "" } md := "" - // First and Previous links - if p.HasPrev { - md += ufmt.Sprintf("[First](?page=1) | ") - md += ufmt.Sprintf("[Previous](?page=%d) | ", p.PageNumber-1) - } else { - md += "First | Previous | " - } - // Page number links - startPage := max(1, p.PageNumber-2) - endPage := min(p.TotalPages, p.PageNumber+2) + if p.PageNumber > 1 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) + } - if startPage > 1 { + if p.PageNumber > 2 { md += "… | " } - for i := startPage; i <= endPage; i++ { - if i == p.PageNumber { - md += ufmt.Sprintf("**%d** | ", i) - } else { - md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) - } + if p.HasPrev { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + } + + md += ufmt.Sprintf("**%d** | ", p.PageNumber) + + if p.HasNext { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) } - if endPage < p.TotalPages { + if p.PageNumber < p.TotalPages-1 { md += "… | " } - // Next and Last links - if p.HasNext { - md += ufmt.Sprintf("[Next](?page=%d) | ", p.PageNumber+1) - md += ufmt.Sprintf("[Last](?page=%d)", p.TotalPages) - } else { - md += "Next | Last" + if p.PageNumber < p.TotalPages { + md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) } return md } +// ParseQuery parses the URL to extract the page number and page size. +func (p *Pager) ParseQuery(rawURL string) (int, int, error) { + u, err := url.Parse(rawURL) + if err != nil { + return 1, p.DefaultPageSize, err + } + + query := u.Query() + pageNumber := 1 + pageSize := p.DefaultPageSize + + if pageStr := query.Get(p.PageQueryParam); pageStr != "" { + pageNumber, err = strconv.Atoi(pageStr) + if err != nil || pageNumber < 1 { + pageNumber = 1 + } + } + + if sizeStr := query.Get("size"); sizeStr != "" { + pageSize, err = strconv.Atoi(sizeStr) + if err != nil || pageSize < 1 { + pageSize = p.DefaultPageSize + } + } + + return pageNumber, pageSize, nil +} + func max(a, b int) int { if a > b { return a diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index ac8d0add82f..502a898dd86 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -19,7 +19,7 @@ func TestPager_GetPage(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases. tests := []struct { @@ -43,8 +43,8 @@ func TestPager_GetPage(t *testing.T) { uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Key, tt.expected[i].Key)) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Value, tt.expected[i].Value)) + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) } } } @@ -59,7 +59,7 @@ func TestPage_RenderSelector(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases. tests := []struct { @@ -67,21 +67,21 @@ func TestPage_RenderSelector(t *testing.T) { pageSize int expected string }{ - {1, 2, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, - {2, 2, "[First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, - {3, 2, "[First](?page=1) | [Previous](?page=2) | … | [2](?page=2) | **3** | Next | Last"}, + {1, 2, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, + {2, 2, "[1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, + {3, 2, "[1](?page=1) | … | [2](?page=2) | **3**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector() + md := page.RenderSelector(pager.PageQueryParam) uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } -func TestPage_RenderSelector_WithManyPages(t *testing.T) { +func TestPager_RenderSelector_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { @@ -89,7 +89,7 @@ func TestPage_RenderSelector_WithManyPages(t *testing.T) { } // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases for a large number of pages. tests := []struct { @@ -97,16 +97,56 @@ func TestPage_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ - {1, 10, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, - {5, 10, "[First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, - {10, 10, "[First](?page=1) | [Previous](?page=9) | … | [8](?page=8) | [9](?page=9) | **10** | Next | Last"}, + {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, + {5, 10, "[1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, + {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector() + md := page.RenderSelector(pager.PageQueryParam) uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } + +func TestPager_ParseQuery(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new pager. + pager := NewPager(tree) + + // Define test cases. + tests := []struct { + rawURL string + expectedPage int + expectedSize int + expectedError bool + }{ + {"https://example.com?size=2&page=1", 1, 2, false}, + {"https://example.com?size=3&page=2", 2, 3, false}, + {"https://example.com?size=5&page=3", 3, 5, false}, + {"https://example.com?page=2", 2, pager.DefaultPageSize, false}, + {"https://example.com?size=3", 1, 3, false}, + {"https://example.com", 1, pager.DefaultPageSize, false}, + {"https://example.com?size=0&page=0", 1, pager.DefaultPageSize, false}, + } + + for _, tt := range tests { + page, size, err := pager.ParseQuery(tt.rawURL) + if tt.expectedError { + uassert.Error(t, err, ufmt.Sprintf("ParseQuery(%s) expected error but got none", tt.rawURL)) + } else { + urequire.NoError(t, err, ufmt.Sprintf("ParseQuery(%s) returned error: %v", tt.rawURL, err)) + uassert.Equal(t, tt.expectedPage, page, ufmt.Sprintf("ParseQuery(%s) returned page %d, expected %d", tt.rawURL, page, tt.expectedPage)) + uassert.Equal(t, tt.expectedSize, size, ufmt.Sprintf("ParseQuery(%s) returned size %d, expected %d", tt.rawURL, size, tt.expectedSize)) + } + } +} diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 43437d5f27b..14213b942f1 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -1,8 +1,6 @@ package main import ( - "fmt" - "gno.land/p/demo/avl" "gno.land/p/demo/avl/pager" "gno.land/p/demo/seqid" @@ -14,12 +12,11 @@ func main() { var id seqid.ID tree := avl.NewTree() for i := 0; i < 42; i++ { - // tree.Set(fmt.Sprintf("key%d", i), i) tree.Set(id.Next().String(), i) } // Create a new pager. - pager := pager.Pager{Tree: tree} + pager := pager.NewPager(tree) for pn := -1; pn < 8; pn++ { page, err := pager.GetPage(pn, 7) @@ -28,14 +25,14 @@ func main() { return } - // Print the items on the first page. - println(ufmt.Sprintf("Page %d of %d\n", page.PageNumber, page.TotalPages)) + // Print the items on the page. + println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) for _, item := range page.Items { - println(fmt.Sprintf("Key: %s, Value: %v\n", item.Key, item.Value)) + println(ufmt.Sprintf("Key: %s, Value: %v", item.Key, item.Value)) } // Render and print the page selector. - pageSelector := page.RenderSelector() + pageSelector := page.RenderSelector(pager.PageQueryParam) println("Page Selector:") println(pageSelector) println() @@ -44,130 +41,79 @@ func main() { // Output: // Page 0 of 6 -// // Page Selector: -// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// **0** | [1](?page=1) | … | [6](?page=6) // // Page 0 of 6 -// // Page Selector: -// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// **0** | [1](?page=1) | … | [6](?page=6) // // Page 1 of 6 -// -// Key: 0000001, Value: 0 -// -// Key: 0000002, Value: 1 -// -// Key: 0000003, Value: 2 -// -// Key: 0000004, Value: 3 -// -// Key: 0000005, Value: 4 -// -// Key: 0000006, Value: 5 -// -// Key: 0000007, Value: 6 -// +// Key: 0000001, Value: (unhandled) +// Key: 0000002, Value: (unhandled) +// Key: 0000003, Value: (unhandled) +// Key: 0000004, Value: (unhandled) +// Key: 0000005, Value: (unhandled) +// Key: 0000006, Value: (unhandled) +// Key: 0000007, Value: (unhandled) // Page Selector: -// First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=6) +// **1** | [2](?page=2) | … | [6](?page=6) // // Page 2 of 6 -// -// Key: 0000008, Value: 7 -// -// Key: 0000009, Value: 8 -// -// Key: 000000a, Value: 9 -// -// Key: 000000b, Value: 10 -// -// Key: 000000c, Value: 11 -// -// Key: 000000d, Value: 12 -// -// Key: 000000e, Value: 13 -// +// Key: 0000008, Value: (unhandled) +// Key: 0000009, Value: (unhandled) +// Key: 000000a, Value: (unhandled) +// Key: 000000b, Value: (unhandled) +// Key: 000000c, Value: (unhandled) +// Key: 000000d, Value: (unhandled) +// Key: 000000e, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [Next](?page=3) | [Last](?page=6) +// [1](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [6](?page=6) // // Page 3 of 6 -// -// Key: 000000f, Value: 14 -// -// Key: 000000g, Value: 15 -// -// Key: 000000h, Value: 16 -// -// Key: 000000j, Value: 17 -// -// Key: 000000k, Value: 18 -// -// Key: 000000m, Value: 19 -// -// Key: 000000n, Value: 20 -// +// Key: 000000f, Value: (unhandled) +// Key: 000000g, Value: (unhandled) +// Key: 000000h, Value: (unhandled) +// Key: 000000j, Value: (unhandled) +// Key: 000000k, Value: (unhandled) +// Key: 000000m, Value: (unhandled) +// Key: 000000n, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=2) | [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [Next](?page=4) | [Last](?page=6) +// [1](?page=1) | … | [2](?page=2) | **3** | [4](?page=4) | … | [6](?page=6) // // Page 4 of 6 -// -// Key: 000000p, Value: 21 -// -// Key: 000000q, Value: 22 -// -// Key: 000000r, Value: 23 -// -// Key: 000000s, Value: 24 -// -// Key: 000000t, Value: 25 -// -// Key: 000000v, Value: 26 -// -// Key: 000000w, Value: 27 -// +// Key: 000000p, Value: (unhandled) +// Key: 000000q, Value: (unhandled) +// Key: 000000r, Value: (unhandled) +// Key: 000000s, Value: (unhandled) +// Key: 000000t, Value: (unhandled) +// Key: 000000v, Value: (unhandled) +// Key: 000000w, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=3) | … | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | [Next](?page=5) | [Last](?page=6) +// [1](?page=1) | … | [3](?page=3) | **4** | [5](?page=5) | … | [6](?page=6) // // Page 5 of 6 -// -// Key: 000000x, Value: 28 -// -// Key: 000000y, Value: 29 -// -// Key: 000000z, Value: 30 -// -// Key: 0000010, Value: 31 -// -// Key: 0000011, Value: 32 -// -// Key: 0000012, Value: 33 -// -// Key: 0000013, Value: 34 -// +// Key: 000000x, Value: (unhandled) +// Key: 000000y, Value: (unhandled) +// Key: 000000z, Value: (unhandled) +// Key: 0000010, Value: (unhandled) +// Key: 0000011, Value: (unhandled) +// Key: 0000012, Value: (unhandled) +// Key: 0000013, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [Next](?page=6) | [Last](?page=6) +// [1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [6](?page=6) // // Page 6 of 6 -// -// Key: 0000014, Value: 35 -// -// Key: 0000015, Value: 36 -// -// Key: 0000016, Value: 37 -// -// Key: 0000017, Value: 38 -// -// Key: 0000018, Value: 39 -// -// Key: 0000019, Value: 40 -// -// Key: 000001a, Value: 41 -// +// Key: 0000014, Value: (unhandled) +// Key: 0000015, Value: (unhandled) +// Key: 0000016, Value: (unhandled) +// Key: 0000017, Value: (unhandled) +// Key: 0000018, Value: (unhandled) +// Key: 0000019, Value: (unhandled) +// Key: 000001a, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=5) | … | [4](?page=4) | [5](?page=5) | **6** | Next | Last +// [1](?page=1) | … | [5](?page=5) | **6** | // // Page 7 of 6 -// // Page Selector: -// [First](?page=1) | [Previous](?page=6) | … | [5](?page=5) | [6](?page=6) | Next | Last +// [1](?page=1) | … | [6](?page=6) | **7** | From 6038562eb915e89a38f22d3070b0f931901dcd0b Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 10:38:07 +0200 Subject: [PATCH 05/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/pager_test.gno | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 502a898dd86..827c923347a 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -40,11 +40,11 @@ func TestPager_GetPage(t *testing.T) { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) + uassert.Equal(t, len(tt.expected), len(page.Items)) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Key, item.Key) + uassert.Equal(t, tt.expected[i].Value, item.Value) } } } @@ -67,17 +67,17 @@ func TestPage_RenderSelector(t *testing.T) { pageSize int expected string }{ - {1, 2, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, - {2, 2, "[1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, - {3, 2, "[1](?page=1) | … | [2](?page=2) | **3**"}, + {1, 2, "**1** | [2](?page=2) | [3](?page=3)"}, + {2, 2, "[1](?page=1) | **2** | [3](?page=3)"}, + {3, 2, "[1](?page=1) | [2](?page=2) | **3**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + urequire.NoError(t, err) md := page.RenderSelector(pager.PageQueryParam) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + uassert.Equal(t, tt.expected, md) } } @@ -97,9 +97,16 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ - {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, - {5, 10, "[1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, - {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, + {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"}, + {2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"}, + {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, + {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, + {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, + {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, + {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } for _, tt := range tests { @@ -107,7 +114,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) md := page.RenderSelector(pager.PageQueryParam) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + uassert.Equal(t, tt.expected, md) } } From 23db6aafbaaa3305aaf57ef94c5f80ab7c05225d Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 10:55:05 +0200 Subject: [PATCH 06/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 47 +++++++++++++------ .../gno.land/p/demo/avl/pager/pager_test.gno | 8 ++-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 18 +++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index ad5234254bf..8a752fde5ef 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -106,30 +106,47 @@ func (p *Page) RenderSelector(queryParam string) string { md := "" - // Page number links - if p.PageNumber > 1 { + if p.HasPrev { + // Always show the first page link md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) - } - if p.PageNumber > 2 { - md += "… | " - } + // Before + if p.PageNumber == 5 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, queryParam, 2) + } else if p.PageNumber > 4 { + md += "… | " + } - if p.HasPrev { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + if p.PageNumber > 3 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, queryParam, p.PageNumber-2) + } + + if p.PageNumber > 2 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + } } - md += ufmt.Sprintf("**%d** | ", p.PageNumber) + // Current page + md += ufmt.Sprintf("**%d**", p.PageNumber) if p.HasNext { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) - } + md += " | " - if p.PageNumber < p.TotalPages-1 { - md += "… | " - } + if p.PageNumber < p.TotalPages-1 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) + } + + if p.PageNumber < p.TotalPages-2 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, queryParam, p.PageNumber+2) + } + + if p.PageNumber == p.TotalPages-4 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, queryParam, p.PageNumber+3) + } else if p.PageNumber < p.TotalPages-3 { + md += "… | " + } - if p.PageNumber < p.TotalPages { + // Always show the last page link md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) } diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 827c923347a..3d72989d429 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -102,10 +102,10 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, - {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, - {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, - {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, - {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, + {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 14213b942f1..b8a8609e70a 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -42,11 +42,11 @@ func main() { // Output: // Page 0 of 6 // Page Selector: -// **0** | [1](?page=1) | … | [6](?page=6) +// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // // Page 0 of 6 // Page Selector: -// **0** | [1](?page=1) | … | [6](?page=6) +// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // // Page 1 of 6 // Key: 0000001, Value: (unhandled) @@ -57,7 +57,7 @@ func main() { // Key: 0000006, Value: (unhandled) // Key: 0000007, Value: (unhandled) // Page Selector: -// **1** | [2](?page=2) | … | [6](?page=6) +// **1** | [2](?page=2) | [3](?page=3) | … | [6](?page=6) // // Page 2 of 6 // Key: 0000008, Value: (unhandled) @@ -68,7 +68,7 @@ func main() { // Key: 000000d, Value: (unhandled) // Key: 000000e, Value: (unhandled) // Page Selector: -// [1](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [6](?page=6) +// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | [5](?page=5) | [6](?page=6) // // Page 3 of 6 // Key: 000000f, Value: (unhandled) @@ -79,7 +79,7 @@ func main() { // Key: 000000m, Value: (unhandled) // Key: 000000n, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [2](?page=2) | **3** | [4](?page=4) | … | [6](?page=6) +// [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | [6](?page=6) // // Page 4 of 6 // Key: 000000p, Value: (unhandled) @@ -90,7 +90,7 @@ func main() { // Key: 000000v, Value: (unhandled) // Key: 000000w, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [3](?page=3) | **4** | [5](?page=5) | … | [6](?page=6) +// [1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) // // Page 5 of 6 // Key: 000000x, Value: (unhandled) @@ -101,7 +101,7 @@ func main() { // Key: 0000012, Value: (unhandled) // Key: 0000013, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [6](?page=6) +// [1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) // // Page 6 of 6 // Key: 0000014, Value: (unhandled) @@ -112,8 +112,8 @@ func main() { // Key: 0000019, Value: (unhandled) // Key: 000001a, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [5](?page=5) | **6** | +// [1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** // // Page 7 of 6 // Page Selector: -// [1](?page=1) | … | [6](?page=6) | **7** | +// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** From fa7286b150f01bd978b800ca83fdb27c09adbd29 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:47:17 +0200 Subject: [PATCH 07/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/pager_test.gno | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 3d72989d429..b6d3086fa99 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -49,6 +49,38 @@ func TestPager_GetPage(t *testing.T) { } } +func TestPager_GetPageByPath(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + for i := 0; i < 50; i++ { + tree.Set(ufmt.Sprintf("key%d", i), i) + } + + // Create a new pager. + pager := NewPager(tree) + + // Define test cases. + tests := []struct { + rawURL string + expectedPage int + expectedSize int + }{ + {"https://example.com?size=10&page=1", 1, 10}, + {"https://example.com?size=10&page=2", 2, 10}, + {"https://example.com?page=3", 3, pager.DefaultPageSize}, + {"https://example.com?size=20", 1, 20}, + {"https://example.com", 1, pager.DefaultPageSize}, + } + + for _, tt := range tests { + page, err := pager.GetPageByPath(tt.rawURL) + urequire.NoError(t, err, ufmt.Sprintf("GetPageByPath(%s) returned error: %v", tt.rawURL, err)) + + uassert.Equal(t, tt.expectedPage, page.PageNumber) + uassert.Equal(t, tt.expectedSize, page.PageSize) + } +} + func TestPage_RenderSelector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() @@ -76,7 +108,7 @@ func TestPage_RenderSelector(t *testing.T) { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err) - md := page.RenderSelector(pager.PageQueryParam) + md := page.RenderSelector() uassert.Equal(t, tt.expected, md) } } @@ -106,14 +138,14 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, - {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, + {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector(pager.PageQueryParam) + md := page.RenderSelector() uassert.Equal(t, tt.expected, md) } } From c0d435dac622c95e27ba5c29a4338f6cba7ede11 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:13:37 +0200 Subject: [PATCH 08/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 31 +++++++++++++------ .../gno.land/p/demo/avl/pager/pager_test.gno | 26 ++++++++-------- .../gno.land/p/demo/avl/pager/z_filetest.gno | 2 +- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 8a752fde5ef..d6be969d233 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -25,6 +25,7 @@ type Page struct { TotalPages int HasPrev bool HasNext bool + Pager *Pager // Reference to the parent Pager } // Item represents a key-value pair in the AVL tree. @@ -60,6 +61,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: false, HasNext: pageNumber < totalPages, + Pager: p, }, nil } @@ -72,6 +74,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: totalPages > 0, HasNext: false, + Pager: p, }, nil } @@ -95,11 +98,21 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: pageNumber > 1, HasNext: pageNumber < totalPages, + Pager: p, }, nil } +// GetPageByPath retrieves a page of results based on the query parameters in the URL path. +func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { + pageNumber, pageSize, err := p.ParseQuery(rawURL) + if err != nil { + return nil, err + } + return p.GetPage(pageNumber, pageSize) +} + // RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector(queryParam string) string { +func (p *Page) RenderSelector() string { if p.TotalPages <= 1 { return "" } @@ -108,21 +121,21 @@ func (p *Page) RenderSelector(queryParam string) string { if p.HasPrev { // Always show the first page link - md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1) // Before if p.PageNumber == 5 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, queryParam, 2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, p.Pager.PageQueryParam, 2) } else if p.PageNumber > 4 { md += "… | " } if p.PageNumber > 3 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, queryParam, p.PageNumber-2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2) } if p.PageNumber > 2 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1) } } @@ -133,21 +146,21 @@ func (p *Page) RenderSelector(queryParam string) string { md += " | " if p.PageNumber < p.TotalPages-1 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1) } if p.PageNumber < p.TotalPages-2 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, queryParam, p.PageNumber+2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2) } if p.PageNumber == p.TotalPages-4 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, queryParam, p.PageNumber+3) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, p.Pager.PageQueryParam, p.PageNumber+3) } else if p.PageNumber < p.TotalPages-3 { md += "… | " } // Always show the last page link - md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) + md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages) } return md diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index b6d3086fa99..1d392585d1f 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -65,11 +65,11 @@ func TestPager_GetPageByPath(t *testing.T) { expectedPage int expectedSize int }{ - {"https://example.com?size=10&page=1", 1, 10}, - {"https://example.com?size=10&page=2", 2, 10}, - {"https://example.com?page=3", 3, pager.DefaultPageSize}, - {"https://example.com?size=20", 1, 20}, - {"https://example.com", 1, pager.DefaultPageSize}, + {"/r/foo:bar/baz?size=10&page=1", 1, 10}, + {"/r/foo:bar/baz?size=10&page=2", 2, 10}, + {"/r/foo:bar/baz?page=3", 3, pager.DefaultPageSize}, + {"/r/foo:bar/baz?size=20", 1, 20}, + {"/r/foo:bar/baz", 1, pager.DefaultPageSize}, } for _, tt := range tests { @@ -138,7 +138,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, - {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, + {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } for _, tt := range tests { @@ -169,13 +169,13 @@ func TestPager_ParseQuery(t *testing.T) { expectedSize int expectedError bool }{ - {"https://example.com?size=2&page=1", 1, 2, false}, - {"https://example.com?size=3&page=2", 2, 3, false}, - {"https://example.com?size=5&page=3", 3, 5, false}, - {"https://example.com?page=2", 2, pager.DefaultPageSize, false}, - {"https://example.com?size=3", 1, 3, false}, - {"https://example.com", 1, pager.DefaultPageSize, false}, - {"https://example.com?size=0&page=0", 1, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=2&page=1", 1, 2, false}, + {"/r/foo:bar/baz?size=3&page=2", 2, 3, false}, + {"/r/foo:bar/baz?size=5&page=3", 3, 5, false}, + {"/r/foo:bar/baz?page=2", 2, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=3", 1, 3, false}, + {"/r/foo:bar/baz", 1, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=0&page=0", 1, pager.DefaultPageSize, false}, } for _, tt := range tests { diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index b8a8609e70a..dafe44a9c15 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -32,7 +32,7 @@ func main() { } // Render and print the page selector. - pageSelector := page.RenderSelector(pager.PageQueryParam) + pageSelector := page.RenderSelector() println("Page Selector:") println(pageSelector) println() From 1cff9f1916d39a7428295390ff0038fa65896a17 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:33:30 +0200 Subject: [PATCH 09/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 34 +++++++++++++++---- .../gno.land/r/demo/users/z_5_filetest.gno | 6 +++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 9b8e93b579b..ed98fc3f25f 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -7,6 +7,7 @@ import ( "strings" "gno.land/p/demo/avl" + "gno.land/p/demo/avl/pager" "gno.land/p/demo/users" ) @@ -294,9 +295,10 @@ var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{5,16}$`) //---------------------------------------- // Render main page -func Render(path string) string { +func Render(fullPath string) string { + path, qs := splitPathAndQuery(fullPath) if path == "" { - return renderHome() + return renderHome(fullPath) } else if len(path) >= 38 { // 39? 40? if path[:2] != "g1" { return "invalid address " + path @@ -316,12 +318,30 @@ func Render(path string) string { } } -func renderHome() string { +func renderHome(path string) string { doc := "" - name2User.Iterate("", "", func(key string, value interface{}) bool { - user := value.(*users.User) + + p := pager.NewPager(&name2User) + page, err := p.GetPageByPath(path) + if err != nil { + panic("pager error: " + err.Error()) + } + + for _, item := range page.Items { + user := item.Value.(*users.User) doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" - return false - }) + } + + doc += page.RenderSelector() return doc } + +func splitPathAndQuery(fullPath string) (string, string) { + parts := strings.SplitN(fullPath, "?", 2) + path := parts[0] + queryString := "" + if len(parts) > 1 { + queryString = "?" + parts[1] + } + return path, queryString +} diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index 4ab68ec0e0b..ba360a11267 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -28,6 +28,8 @@ func main() { users.Register(caller, "satoshi", "my other profile") println(users.Render("")) println("========================================") + println(users.Render("?page=2")) + println("========================================") println(users.Render("gnouser")) println("========================================") println(users.Render("satoshi")) @@ -46,8 +48,10 @@ func main() { // * [nt](/r/demo/users:nt) // * [satoshi](/r/demo/users:satoshi) // * [sys](/r/demo/users:sys) +// **1** | [2](?page=2) +// ======================================== // * [x](/r/demo/users:x) -// +// [1](?page=1) | **2** // ======================================== // ## user gnouser // From b2294ac3bac52e62b634542e159f3384c4f5eea8 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:47:46 +0200 Subject: [PATCH 10/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/gno.mod | 1 + examples/gno.land/r/demo/users/users.gno | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/gno.mod b/examples/gno.land/r/demo/users/gno.mod index a2ee2ea86ba..c47eaf3bd28 100644 --- a/examples/gno.land/r/demo/users/gno.mod +++ b/examples/gno.land/r/demo/users/gno.mod @@ -2,5 +2,6 @@ module gno.land/r/demo/users require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/avl/pager v0.0.0-latest gno.land/p/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index ed98fc3f25f..ce5fbdd7742 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -296,7 +296,7 @@ var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{5,16}$`) // Render main page func Render(fullPath string) string { - path, qs := splitPathAndQuery(fullPath) + path, _ := splitPathAndQuery(fullPath) if path == "" { return renderHome(fullPath) } else if len(path) >= 38 { // 39? 40? From 2a1093ed5d2c97719fd34c9ff9e450ec80737cc6 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:57:10 +0200 Subject: [PATCH 11/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 22 +++++++++++-------- .../gno.land/p/demo/avl/pager/pager_test.gno | 6 ++--- examples/gno.land/r/demo/users/users.gno | 5 +---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index d6be969d233..4e6dde41a71 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -44,11 +44,7 @@ func NewPager(tree *avl.Tree) *Pager { } // GetPage retrieves a page of results from the AVL tree. -func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { - if pageSize < 1 { - return nil, ufmt.Errorf("invalid page size") - } - +func (p *Pager) GetPage(pageNumber int, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) @@ -62,7 +58,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: false, HasNext: pageNumber < totalPages, Pager: p, - }, nil + } } if pageNumber > totalPages { @@ -75,7 +71,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: totalPages > 0, HasNext: false, Pager: p, - }, nil + } } startIndex := (pageNumber - 1) * pageSize @@ -99,7 +95,15 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: pageNumber > 1, HasNext: pageNumber < totalPages, Pager: p, - }, nil + } +} + +func (p *Pager) MustGetPageByPath(rawURL string) *Page { + page, err := p.GetPageByPath(rawURL) + if err != nil { + panic("invalid path") + } + return page } // GetPageByPath retrieves a page of results based on the query parameters in the URL path. @@ -108,7 +112,7 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { if err != nil { return nil, err } - return p.GetPage(pageNumber, pageSize) + return p.GetPage(pageNumber, pageSize), nil } // RenderSelector generates the Markdown for the page selector. diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 1d392585d1f..74ce9b317d4 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -37,8 +37,7 @@ func TestPager_GetPage(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + page := pager.GetPage(tt.pageNumber, tt.pageSize) uassert.Equal(t, len(tt.expected), len(page.Items)) @@ -142,8 +141,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + page := pager.GetPage(tt.pageNumber, tt.pageSize) md := page.RenderSelector() uassert.Equal(t, tt.expected, md) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index ce5fbdd7742..e2cf27d4e8e 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -322,10 +322,7 @@ func renderHome(path string) string { doc := "" p := pager.NewPager(&name2User) - page, err := p.GetPageByPath(path) - if err != nil { - panic("pager error: " + err.Error()) - } + page := p.MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) From 903fb48b451697684bfc118eacaa7e1b6ec4dcd1 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:21:27 +0200 Subject: [PATCH 12/71] Update pager_test.gno --- examples/gno.land/p/demo/avl/pager/pager_test.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 74ce9b317d4..4cf7446c4cc 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -104,8 +104,7 @@ func TestPage_RenderSelector(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err) + page := pager.GetPage(tt.pageNumber, tt.pageSize) md := page.RenderSelector() uassert.Equal(t, tt.expected, md) From f1adda04961a7932677895132b51a50fa1588d10 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:41:53 +0200 Subject: [PATCH 13/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/z_filetest.gno | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index dafe44a9c15..00da1aa3537 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -19,11 +19,7 @@ func main() { pager := pager.NewPager(tree) for pn := -1; pn < 8; pn++ { - page, err := pager.GetPage(pn, 7) - if err != nil { - println("Error:", err.Error()) - return - } + page := pager.GetPage(pn, 7) // Print the items on the page. println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) From 5d8a12be1dd2b0e29ee751df986d56be3b4baeb5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 24 Sep 2024 13:08:39 +0200 Subject: [PATCH 14/71] add initial hof --- examples/gno.land/r/gnoland/hof/gno.mod | 1 + examples/gno.land/r/gnoland/hof/hof.gno | 49 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 examples/gno.land/r/gnoland/hof/gno.mod create mode 100644 examples/gno.land/r/gnoland/hof/hof.gno diff --git a/examples/gno.land/r/gnoland/hof/gno.mod b/examples/gno.land/r/gnoland/hof/gno.mod new file mode 100644 index 00000000000..6bc1758ea4d --- /dev/null +++ b/examples/gno.land/r/gnoland/hof/gno.mod @@ -0,0 +1 @@ +module gno.land/r/gnoland/hof \ No newline at end of file diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno new file mode 100644 index 00000000000..3d805391462 --- /dev/null +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -0,0 +1,49 @@ +// Package hof represents the gno.land hall of fame realm. +// It contains a permanent exhibition of items in the hall of fame, +// a possibility to host temporary ones created by its admins. +package hof + +import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "time" +) + +var ( + permanent *Exhibition + temporary *avl.Tree + idCounter seqid.ID +) + +type Exhibition struct { + id seqid.ID + + title string + description string + startTime time.Time // given in RFC3339 + endTime time.Time // end time of the event, given in RFC3339 + items *avl.Tree +} + +func init() { + permanent = &Exhibition{ + id: idCounter.Next(), + title: "Hall Of Fame", + description: "gno.land's hall of fame!", + startTime: time.Now(), + endTime: time.Now(), + items: avl.NewTree(), + } + + temporary = avl.NewTree() + +} + +func Register() { + permanent + +} + +func RegisterTemp(id seqid.ID) { + +} From 538f6f14da3f6beb2b13dc3f62c250df4dae7e25 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 24 Sep 2024 15:31:32 +0200 Subject: [PATCH 15/71] add --- examples/gno.land/r/gnoland/hof/hof.gno | 54 ++++++++++++++++++------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 3d805391462..93a202e541e 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -1,4 +1,4 @@ -// Package hof represents the gno.land hall of fame realm. +// Package hof is the gno.land hall of fame realm. // It contains a permanent exhibition of items in the hall of fame, // a possibility to host temporary ones created by its admins. package hof @@ -6,24 +6,31 @@ package hof import ( "github.com/gnolang/gno/examples/gno.land/p/demo/avl" "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/gnovm/stdlibs/std" "time" ) var ( + idCounter seqid.ID permanent *Exhibition temporary *avl.Tree - idCounter seqid.ID ) -type Exhibition struct { - id seqid.ID +type ( + Exhibition struct { + id seqid.ID + title string + description string + startTime time.Time // given in RFC3339 + endTime time.Time // end time of the event, given in RFC3339 + items *avl.Tree // submission id > + } - title string - description string - startTime time.Time // given in RFC3339 - endTime time.Time // end time of the event, given in RFC3339 - items *avl.Tree -} + pair struct { + deployer std.Address + renderDescription func() string + } +) func init() { permanent = &Exhibition{ @@ -36,14 +43,33 @@ func init() { } temporary = avl.NewTree() - } -func Register() { - permanent +func Register(rDesc func() string) { + submission := std.PrevRealm() + pkgpath := submission.PkgPath() + + // must not exist and must be called from code + if permanent.items.Has(pkgpath) || submission.IsUser() { + return + } + + permanent.items.Set(pkgpath, pair{ + deployer: std.GetOrigCaller(), + renderDescription: rDesc, + }) } -func RegisterTemp(id seqid.ID) { +func Render(_ string) string { + out := "" + + permanent.items.Iterate("", "", func(key string, value interface{}) bool { + rFunc := value.(rndr) + out += rFunc("") + + return false + }) + return out } From c8e4e1fd958f17f8873b6c10d7c79809ea94ccb5 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:29:19 +0200 Subject: [PATCH 16/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 53 ++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 19 +-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 134 ++++++++---------- 3 files changed, 102 insertions(+), 104 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 4e6dde41a71..511c35905d5 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -13,6 +13,7 @@ import ( type Pager struct { Tree *avl.Tree PageQueryParam string + SizeQueryParam string DefaultPageSize int } @@ -39,6 +40,7 @@ func NewPager(tree *avl.Tree) *Pager { return &Pager{ Tree: tree, PageQueryParam: "page", + SizeQueryParam: "size", DefaultPageSize: 10, } } @@ -115,8 +117,11 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { return p.GetPage(pageNumber, pageSize), nil } -// RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector() string { +// UI generates the Markdown UI for the page selector. +func (p *Page) Selector() string { + pageNumber := p.PageNumber + pageNumber = max(pageNumber, 1) + if p.TotalPages <= 1 { return "" } @@ -128,9 +133,7 @@ func (p *Page) RenderSelector() string { md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1) // Before - if p.PageNumber == 5 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, p.Pager.PageQueryParam, 2) - } else if p.PageNumber > 4 { + if p.PageNumber > 4 { md += "… | " } @@ -143,8 +146,12 @@ func (p *Page) RenderSelector() string { } } - // Current page - md += ufmt.Sprintf("**%d**", p.PageNumber) + if p.PageNumber > 0 && p.PageNumber <= p.TotalPages { + // Current page + md += ufmt.Sprintf("**%d**", p.PageNumber) + } else { + md += ufmt.Sprintf("_%d_", p.PageNumber) + } if p.HasNext { md += " | " @@ -157,9 +164,7 @@ func (p *Page) RenderSelector() string { md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2) } - if p.PageNumber == p.TotalPages-4 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, p.Pager.PageQueryParam, p.PageNumber+3) - } else if p.PageNumber < p.TotalPages-3 { + if p.PageNumber < p.TotalPages-3 { md += "… | " } @@ -181,32 +186,36 @@ func (p *Pager) ParseQuery(rawURL string) (int, int, error) { pageNumber := 1 pageSize := p.DefaultPageSize - if pageStr := query.Get(p.PageQueryParam); pageStr != "" { - pageNumber, err = strconv.Atoi(pageStr) - if err != nil || pageNumber < 1 { - pageNumber = 1 + if p.PageQueryParam != "" { + if pageStr := query.Get(p.PageQueryParam); pageStr != "" { + pageNumber, err = strconv.Atoi(pageStr) + if err != nil || pageNumber < 1 { + pageNumber = 1 + } } } - if sizeStr := query.Get("size"); sizeStr != "" { - pageSize, err = strconv.Atoi(sizeStr) - if err != nil || pageSize < 1 { - pageSize = p.DefaultPageSize + if p.SizeQueryParam != "" { + if sizeStr := query.Get(p.SizeQueryParam); sizeStr != "" { + pageSize, err = strconv.Atoi(sizeStr) + if err != nil || pageSize < 1 { + pageSize = p.DefaultPageSize + } } } return pageNumber, pageSize, nil } -func max(a, b int) int { - if a > b { +func min(a, b int) int { + if a < b { return a } return b } -func min(a, b int) int { - if a < b { +func max(a, b int) int { + if a > b { return a } return b diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 4cf7446c4cc..abb5a46b280 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -80,7 +80,7 @@ func TestPager_GetPageByPath(t *testing.T) { } } -func TestPage_RenderSelector(t *testing.T) { +func TestPage_Selector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -106,12 +106,12 @@ func TestPage_RenderSelector(t *testing.T) { for _, tt := range tests { page := pager.GetPage(tt.pageNumber, tt.pageSize) - md := page.RenderSelector() - uassert.Equal(t, tt.expected, md) + ui := page.Selector() + uassert.Equal(t, tt.expected, ui) } } -func TestPager_RenderSelector_WithManyPages(t *testing.T) { +func TestPager_UI_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { @@ -127,23 +127,26 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ + // XXX: -1 + // XXX: 0 {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"}, {2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"}, {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, - {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, - {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {5, 10, "[1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | … | [10](?page=10)"}, {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, + // XXX: 11 } for _, tt := range tests { page := pager.GetPage(tt.pageNumber, tt.pageSize) - md := page.RenderSelector() - uassert.Equal(t, tt.expected, md) + ui := page.Selector() + uassert.Equal(t, tt.expected, ui) } } diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 00da1aa3537..e531e5557dd 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -21,95 +21,81 @@ func main() { for pn := -1; pn < 8; pn++ { page := pager.GetPage(pn, 7) - // Print the items on the page. - println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) - for _, item := range page.Items { - println(ufmt.Sprintf("Key: %s, Value: %v", item.Key, item.Value)) + println(ufmt.Sprintf("## Page %d of %d", page.PageNumber, page.TotalPages)) + for idx, item := range page.Items { + println(ufmt.Sprintf("- idx=%d key=%s value=%d", idx, item.Key, item.Value)) } - - // Render and print the page selector. - pageSelector := page.RenderSelector() - println("Page Selector:") - println(pageSelector) + println(page.Selector()) println() } } // Output: -// Page 0 of 6 -// Page Selector: -// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) +// ## Page 0 of 6 +// _0_ | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // -// Page 0 of 6 -// Page Selector: -// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) +// ## Page 0 of 6 +// _0_ | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // -// Page 1 of 6 -// Key: 0000001, Value: (unhandled) -// Key: 0000002, Value: (unhandled) -// Key: 0000003, Value: (unhandled) -// Key: 0000004, Value: (unhandled) -// Key: 0000005, Value: (unhandled) -// Key: 0000006, Value: (unhandled) -// Key: 0000007, Value: (unhandled) -// Page Selector: +// ## Page 1 of 6 +// - idx=0 key=0000001 value=0 +// - idx=1 key=0000002 value=1 +// - idx=2 key=0000003 value=2 +// - idx=3 key=0000004 value=3 +// - idx=4 key=0000005 value=4 +// - idx=5 key=0000006 value=5 +// - idx=6 key=0000007 value=6 // **1** | [2](?page=2) | [3](?page=3) | … | [6](?page=6) // -// Page 2 of 6 -// Key: 0000008, Value: (unhandled) -// Key: 0000009, Value: (unhandled) -// Key: 000000a, Value: (unhandled) -// Key: 000000b, Value: (unhandled) -// Key: 000000c, Value: (unhandled) -// Key: 000000d, Value: (unhandled) -// Key: 000000e, Value: (unhandled) -// Page Selector: -// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | [5](?page=5) | [6](?page=6) +// ## Page 2 of 6 +// - idx=0 key=0000008 value=7 +// - idx=1 key=0000009 value=8 +// - idx=2 key=000000a value=9 +// - idx=3 key=000000b value=10 +// - idx=4 key=000000c value=11 +// - idx=5 key=000000d value=12 +// - idx=6 key=000000e value=13 +// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [6](?page=6) // -// Page 3 of 6 -// Key: 000000f, Value: (unhandled) -// Key: 000000g, Value: (unhandled) -// Key: 000000h, Value: (unhandled) -// Key: 000000j, Value: (unhandled) -// Key: 000000k, Value: (unhandled) -// Key: 000000m, Value: (unhandled) -// Key: 000000n, Value: (unhandled) -// Page Selector: +// ## Page 3 of 6 +// - idx=0 key=000000f value=14 +// - idx=1 key=000000g value=15 +// - idx=2 key=000000h value=16 +// - idx=3 key=000000j value=17 +// - idx=4 key=000000k value=18 +// - idx=5 key=000000m value=19 +// - idx=6 key=000000n value=20 // [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | [6](?page=6) // -// Page 4 of 6 -// Key: 000000p, Value: (unhandled) -// Key: 000000q, Value: (unhandled) -// Key: 000000r, Value: (unhandled) -// Key: 000000s, Value: (unhandled) -// Key: 000000t, Value: (unhandled) -// Key: 000000v, Value: (unhandled) -// Key: 000000w, Value: (unhandled) -// Page Selector: +// ## Page 4 of 6 +// - idx=0 key=000000p value=21 +// - idx=1 key=000000q value=22 +// - idx=2 key=000000r value=23 +// - idx=3 key=000000s value=24 +// - idx=4 key=000000t value=25 +// - idx=5 key=000000v value=26 +// - idx=6 key=000000w value=27 // [1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) // -// Page 5 of 6 -// Key: 000000x, Value: (unhandled) -// Key: 000000y, Value: (unhandled) -// Key: 000000z, Value: (unhandled) -// Key: 0000010, Value: (unhandled) -// Key: 0000011, Value: (unhandled) -// Key: 0000012, Value: (unhandled) -// Key: 0000013, Value: (unhandled) -// Page Selector: -// [1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) +// ## Page 5 of 6 +// - idx=0 key=000000x value=28 +// - idx=1 key=000000y value=29 +// - idx=2 key=000000z value=30 +// - idx=3 key=0000010 value=31 +// - idx=4 key=0000011 value=32 +// - idx=5 key=0000012 value=33 +// - idx=6 key=0000013 value=34 +// [1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) // -// Page 6 of 6 -// Key: 0000014, Value: (unhandled) -// Key: 0000015, Value: (unhandled) -// Key: 0000016, Value: (unhandled) -// Key: 0000017, Value: (unhandled) -// Key: 0000018, Value: (unhandled) -// Key: 0000019, Value: (unhandled) -// Key: 000001a, Value: (unhandled) -// Page Selector: +// ## Page 6 of 6 +// - idx=0 key=0000014 value=35 +// - idx=1 key=0000015 value=36 +// - idx=2 key=0000016 value=37 +// - idx=3 key=0000017 value=38 +// - idx=4 key=0000018 value=39 +// - idx=5 key=0000019 value=40 +// - idx=6 key=000001a value=41 // [1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** // -// Page 7 of 6 -// Page Selector: -// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** +// ## Page 7 of 6 +// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | _7_ From a0be0358e20889c994cca58fffe1fb85427cee8b Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:07:30 +0200 Subject: [PATCH 17/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 11eff33d72c..baf50f68927 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -336,7 +336,7 @@ func renderHome(path string) string { doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" } - doc += page.RenderSelector() + doc += page.Selector() return doc } From 5c3524dca4d8187600e6c70ed9b2afdd009de156 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:15:35 +0200 Subject: [PATCH 18/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index baf50f68927..652957fb6c0 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,8 +328,7 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - p := pager.NewPager(&name2User) - page := p.MustGetPageByPath(path) + page := pager.NewPager(&name2User).MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) From 17b7ba69c9a66d19b203d78d91db3fd23a84a27f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 25 Sep 2024 15:15:25 +0200 Subject: [PATCH 19/71] add item & exh rendering --- examples/gno.land/r/gnoland/hof/gno.mod | 8 ++- examples/gno.land/r/gnoland/hof/hof.gno | 66 ++++++++++++++++++------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/gno.mod b/examples/gno.land/r/gnoland/hof/gno.mod index 6bc1758ea4d..8950dfedf09 100644 --- a/examples/gno.land/r/gnoland/hof/gno.mod +++ b/examples/gno.land/r/gnoland/hof/gno.mod @@ -1 +1,7 @@ -module gno.land/r/gnoland/hof \ No newline at end of file +module gno.land/r/gnoland/hof + +require ( + gno.land/p/demo/avl 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/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 93a202e541e..b01534b1b40 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -6,6 +6,7 @@ package hof import ( "github.com/gnolang/gno/examples/gno.land/p/demo/avl" "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" "github.com/gnolang/gno/gnovm/stdlibs/std" "time" ) @@ -13,7 +14,6 @@ import ( var ( idCounter seqid.ID permanent *Exhibition - temporary *avl.Tree ) type ( @@ -23,12 +23,15 @@ type ( description string startTime time.Time // given in RFC3339 endTime time.Time // end time of the event, given in RFC3339 - items *avl.Tree // submission id > + items *avl.Tree // Item id > Item } - pair struct { - deployer std.Address - renderDescription func() string + Item struct { + pkgpath string + deployer std.Address + blockNum int64 + likes int + dislikes int } ) @@ -41,35 +44,62 @@ func init() { endTime: time.Now(), items: avl.NewTree(), } - - temporary = avl.NewTree() } -func Register(rDesc func() string) { +func Register() { submission := std.PrevRealm() pkgpath := submission.PkgPath() // must not exist and must be called from code if permanent.items.Has(pkgpath) || submission.IsUser() { - return + panic("item exists or not code call") } - permanent.items.Set(pkgpath, pair{ - deployer: std.GetOrigCaller(), - renderDescription: rDesc, - }) - + permanent.items.Set( + idCounter.Next().String(), + &Item{ + pkgpath: pkgpath, + deployer: std.GetOrigCaller(), + blockNum: std.GetHeight(), + likes: 0, + dislikes: 0, + }) } func Render(_ string) string { - out := "" + out := "# Hall of Fame\n\n" + + out += permanent.Render() + + // render temp exhibitions + + return out +} + +func (e Exhibition) Render() string { + out := ufmt.Sprintf("# %s\n\n", e.title) + out += ufmt.Sprintf("%s\n\n", e.description) - permanent.items.Iterate("", "", func(key string, value interface{}) bool { - rFunc := value.(rndr) - out += rFunc("") + out += "
\n\n" + + e.items.Iterate("", "", func(key string, value interface{}) bool { + out += "
" + //out += ufmt.Sprintf("Submission #%s", key) + out += value.(*Item).Render() + out += "
" return false }) + out += "
\n\n" + + return out +} + +func (i Item) Render() string { + out := ufmt.Sprintf("`%s`\n\n", i.pkgpath) + out += ufmt.Sprintf("Published at block #%d\n\n", i.blockNum) + out += ufmt.Sprintf("Publisher: %s\n\n", i.deployer.String()) + out += ufmt.Sprintf("%d 👍 ---- %d 👎\n\n", i.likes, i.dislikes) return out } From 29bb8dc32f7d8d087fe5d3a9c4c2c5bb57044f42 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 25 Sep 2024 16:16:54 +0200 Subject: [PATCH 20/71] save --- examples/gno.land/r/gnoland/blog/admin.gno | 2 ++ examples/gno.land/r/gnoland/blog/gno.mod | 1 + examples/gno.land/r/gnoland/hof/hof.gno | 28 ++++++++++++---------- examples/gno.land/r/leon/home/gno.mod | 1 + examples/gno.land/r/leon/home/home.gno | 3 +++ 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 08b0911cf24..58e6a0a3f2c 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -7,6 +7,7 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/context" "gno.land/p/gov/proposal" + "gno.land/r/gnoland/hof" ) var ( @@ -19,6 +20,7 @@ var ( func init() { // adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis. adminAddr = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" + hof.Register() } func AdminSetAdminAddr(addr std.Address) { diff --git a/examples/gno.land/r/gnoland/blog/gno.mod b/examples/gno.land/r/gnoland/blog/gno.mod index 17c17e0cfa6..069a6970cd7 100644 --- a/examples/gno.land/r/gnoland/blog/gno.mod +++ b/examples/gno.land/r/gnoland/blog/gno.mod @@ -5,4 +5,5 @@ require ( gno.land/p/demo/blog v0.0.0-latest gno.land/p/demo/context v0.0.0-latest gno.land/p/gov/proposal v0.0.0-latest + gno.land/r/gnoland/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index b01534b1b40..54b5c21dd3b 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -7,7 +7,8 @@ import ( "github.com/gnolang/gno/examples/gno.land/p/demo/avl" "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" - "github.com/gnolang/gno/gnovm/stdlibs/std" + "std" + "strings" "time" ) @@ -38,8 +39,8 @@ type ( func init() { permanent = &Exhibition{ id: idCounter.Next(), - title: "Hall Of Fame", - description: "gno.land's hall of fame!", + title: "Permanent Exhibition", + description: ``, startTime: time.Now(), endTime: time.Now(), items: avl.NewTree(), @@ -71,35 +72,38 @@ func Render(_ string) string { out += permanent.Render() - // render temp exhibitions + // todo render temp exhibitions return out } func (e Exhibition) Render() string { - out := ufmt.Sprintf("# %s\n\n", e.title) + out := ufmt.Sprintf("## %s\n\n", e.title) out += ufmt.Sprintf("%s\n\n", e.description) - out += "
\n\n" + out += "
\n\n" e.items.Iterate("", "", func(key string, value interface{}) bool { - out += "
" - //out += ufmt.Sprintf("Submission #%s", key) + out += "
\n\n" + out += ufmt.Sprintf("### Submission #%s\n\n", strings.Replace(key, "0", "", -1)) out += value.(*Item).Render() out += "
" return false }) - out += "
\n\n" + out += "
\n\n" return out } func (i Item) Render() string { - out := ufmt.Sprintf("`%s`\n\n", i.pkgpath) - out += ufmt.Sprintf("Published at block #%d\n\n", i.blockNum) - out += ufmt.Sprintf("Publisher: %s\n\n", i.deployer.String()) + //subpath := i.pkgpath[strings.Index(i.pkgpath, "/"):] + + out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) + out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) + out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) out += ufmt.Sprintf("%d 👍 ---- %d 👎\n\n", i.likes, i.dislikes) + return out } diff --git a/examples/gno.land/r/leon/home/gno.mod b/examples/gno.land/r/leon/home/gno.mod index 48cf64a9d0a..99e7a48aa90 100644 --- a/examples/gno.land/r/leon/home/gno.mod +++ b/examples/gno.land/r/leon/home/gno.mod @@ -4,5 +4,6 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/r/demo/art/gnoface v0.0.0-latest gno.land/r/demo/art/millipede v0.0.0-latest + gno.land/r/gnoland/hof v0.0.0-latest gno.land/r/leon/config v0.0.0-latest ) diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index 1f6a07e8959..7216ac02aaf 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -8,6 +8,7 @@ import ( "gno.land/r/demo/art/gnoface" "gno.land/r/demo/art/millipede" + "gno.land/r/gnoland/hof" "gno.land/r/leon/config" ) @@ -31,6 +32,8 @@ My contributions to gno.land can mainly be found TODO import r/gh `, } + + hof.Register() } func UpdatePFP(url, caption string) { From 5393e7b3018461c38a706c59555b496164e3d9d7 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 25 Sep 2024 18:21:55 +0200 Subject: [PATCH 21/71] update rendering, add upvote/downvote --- examples/gno.land/r/gnoland/hof/errors.gno | 10 +++ examples/gno.land/r/gnoland/hof/hof.gno | 100 +++++++++++++++------ 2 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 examples/gno.land/r/gnoland/hof/errors.gno diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno new file mode 100644 index 00000000000..4847b5a19cd --- /dev/null +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -0,0 +1,10 @@ +package hof + +import "errors" + +var ( + ErrNoSuchItem = errors.New("hof: no such item exists") + ErrNoSuchExhibition = errors.New("hof: no such exhibition exists") + ErrDoubleUpvote = errors.New("hof: cannot upvote twice") + ErrDoubleDownvote = errors.New("hof: cannot downvote twice") +) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 54b5c21dd3b..5e9a23d671e 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,46 +4,44 @@ package hof import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/avl" - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" - "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + "gno.land/p/demo/avl" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" "std" - "strings" "time" ) var ( - idCounter seqid.ID - permanent *Exhibition + exhibCounter seqid.ID + permanent *Exhibition ) type ( Exhibition struct { - id seqid.ID + id seqid.ID // fixed + itemCounter seqid.ID // title string description string - startTime time.Time // given in RFC3339 - endTime time.Time // end time of the event, given in RFC3339 - items *avl.Tree // Item id > Item + startTime *time.Time // given in RFC3339 + endTime *time.Time // end time of the event, given in RFC3339 + items *avl.Tree // Item id > Item } Item struct { + id seqid.ID pkgpath string deployer std.Address blockNum int64 - likes int - dislikes int + upvote *avl.Tree // std.Addr > struct{}{} + downvote *avl.Tree // std.Addr > struct{}{} } ) func init() { permanent = &Exhibition{ - id: idCounter.Next(), - title: "Permanent Exhibition", - description: ``, - startTime: time.Now(), - endTime: time.Now(), - items: avl.NewTree(), + id: exhibCounter.Next(), + title: "Permanent Exhibition", + items: avl.NewTree(), } } @@ -56,14 +54,16 @@ func Register() { panic("item exists or not code call") } + id := permanent.itemCounter.Next() permanent.items.Set( - idCounter.Next().String(), + id.String(), &Item{ + id: id, pkgpath: pkgpath, deployer: std.GetOrigCaller(), blockNum: std.GetHeight(), - likes: 0, - dislikes: 0, + upvote: avl.NewTree(), + downvote: avl.NewTree(), }) } @@ -78,14 +78,15 @@ func Render(_ string) string { } func (e Exhibition) Render() string { - out := ufmt.Sprintf("## %s\n\n", e.title) - out += ufmt.Sprintf("%s\n\n", e.description) + //out := ufmt.Sprintf("## %s\n\n", e.title) + //out += ufmt.Sprintf("%s\n\n", e.description) - out += "
\n\n" + out := "
\n\n" - e.items.Iterate("", "", func(key string, value interface{}) bool { + e.items.ReverseIterate("", "", func(key string, value interface{}) bool { out += "
\n\n" - out += ufmt.Sprintf("### Submission #%s\n\n", strings.Replace(key, "0", "", -1)) + id, _ := seqid.FromString(key) + out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) out += value.(*Item).Render() out += "
" @@ -103,7 +104,52 @@ func (i Item) Render() string { out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) - out += ufmt.Sprintf("%d 👍 ---- %d 👎\n\n", i.likes, i.dislikes) + out += ufmt.Sprintf(` +#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibitionID=0&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibitionID=0&itemID=%s)`, i.upvote.Size(), i.id.String(), i.downvote.Size(), i.id.String()) return out } + +func Upvote(exhibitionID, itemID seqid.ID) error { + if exhibitionID == seqid.ID(0) { + raw, ok := permanent.items.Get(itemID.String()) + if !ok { + return ErrNoSuchItem + } + + item := raw.(*Item) + caller := std.PrevRealm().Addr().String() + + if item.upvote.Has(caller) { + return ErrDoubleUpvote + } + + item.upvote.Set(caller, struct{}{}) + return nil + } + + // todo add temp exh + return nil +} + +func Downvote(exhibitionID, itemID seqid.ID) error { + if exhibitionID == seqid.ID(0) { + raw, ok := permanent.items.Get(itemID.String()) + if !ok { + return ErrNoSuchItem + } + + item := raw.(*Item) + caller := std.PrevRealm().Addr().String() + + if item.downvote.Has(caller) { + return ErrDoubleDownvote + } + + item.downvote.Set(caller, struct{}{}) + return nil + } + + // todo add temp exh + return nil +} From 9a171ff36277ff848cedb77a277ec63d21760d57 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 26 Sep 2024 10:29:43 +0200 Subject: [PATCH 22/71] register temp --- examples/gno.land/r/gnoland/hof/hof.gno | 30 +++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 5e9a23d671e..ece4f4b65df 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -67,6 +67,28 @@ func Register() { }) } +func RegisterFor(exhibID string) { + submission := std.PrevRealm() + pkgpath := submission.PkgPath() + + // must not exist and must be called from code + if permanent.items.Has(pkgpath) || submission.IsUser() { + panic("item exists or not code call") + } + + id := permanent.itemCounter.Next() + permanent.items.Set( + id.String(), + &Item{ + id: id, + pkgpath: pkgpath, + deployer: std.GetOrigCaller(), + blockNum: std.GetHeight(), + upvote: avl.NewTree(), + downvote: avl.NewTree(), + }) +} + func Render(_ string) string { out := "# Hall of Fame\n\n" @@ -110,8 +132,8 @@ func (i Item) Render() string { return out } -func Upvote(exhibitionID, itemID seqid.ID) error { - if exhibitionID == seqid.ID(0) { +func Upvote(exhibID, itemID seqid.ID) error { + if exhibID == seqid.ID(0) { raw, ok := permanent.items.Get(itemID.String()) if !ok { return ErrNoSuchItem @@ -132,8 +154,8 @@ func Upvote(exhibitionID, itemID seqid.ID) error { return nil } -func Downvote(exhibitionID, itemID seqid.ID) error { - if exhibitionID == seqid.ID(0) { +func Downvote(exhibID, itemID seqid.ID) error { + if exhibID == seqid.ID(0) { raw, ok := permanent.items.Get(itemID.String()) if !ok { return ErrNoSuchItem From 1da88059f98f762eb6e57e142fdcab0dfef83b36 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:16:21 +0200 Subject: [PATCH 23/71] chore: new API Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 25 ++++++++++++++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 16 ++++++------ .../gno.land/p/demo/avl/pager/z_filetest.gno | 4 +-- examples/gno.land/r/demo/users/users.gno | 2 +- .../gno.land/r/demo/users/z_5_filetest.gno | 6 ++--- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 511c35905d5..fb2e979c51b 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -36,20 +36,37 @@ type Item struct { } // NewPager creates a new Pager with default values. -func NewPager(tree *avl.Tree) *Pager { +func NewPager(tree *avl.Tree, defaultPageSize int) *Pager { return &Pager{ Tree: tree, PageQueryParam: "page", SizeQueryParam: "size", - DefaultPageSize: 10, + DefaultPageSize: defaultPageSize, } } // GetPage retrieves a page of results from the AVL tree. -func (p *Pager) GetPage(pageNumber int, pageSize int) *Page { +func (p *Pager) GetPage(pageNumber int) *Page { + return p.GetPageWithSize(pageNumber, p.DefaultPageSize) +} + +func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + if pageSize < 1 { + return &Page{ + Items: []Item{}, + PageNumber: 0, + PageSize: 0, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: false, + HasNext: false, + Pager: p, + } + } + if pageNumber < 1 { return &Page{ Items: []Item{}, @@ -114,7 +131,7 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { if err != nil { return nil, err } - return p.GetPage(pageNumber, pageSize), nil + return p.GetPageWithSize(pageNumber, pageSize), nil } // UI generates the Markdown UI for the page selector. diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index abb5a46b280..da4680db8c7 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -19,7 +19,7 @@ func TestPager_GetPage(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -37,7 +37,7 @@ func TestPager_GetPage(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) uassert.Equal(t, len(tt.expected), len(page.Items)) @@ -56,7 +56,7 @@ func TestPager_GetPageByPath(t *testing.T) { } // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -90,7 +90,7 @@ func TestPage_Selector(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -104,7 +104,7 @@ func TestPage_Selector(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) ui := page.Selector() uassert.Equal(t, tt.expected, ui) @@ -119,7 +119,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { } // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases for a large number of pages. tests := []struct { @@ -143,7 +143,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) ui := page.Selector() uassert.Equal(t, tt.expected, ui) @@ -160,7 +160,7 @@ func TestPager_ParseQuery(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index e531e5557dd..91c20115469 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -16,10 +16,10 @@ func main() { } // Create a new pager. - pager := pager.NewPager(tree) + pager := pager.NewPager(tree, 7) for pn := -1; pn < 8; pn++ { - page := pager.GetPage(pn, 7) + page := pager.GetPage(pn) println(ufmt.Sprintf("## Page %d of %d", page.PageNumber, page.TotalPages)) for idx, item := range page.Items { diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 62b422558db..2655d299ef7 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,7 +328,7 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - page := pager.NewPager(&name2User).MustGetPageByPath(path) + page := pager.NewPager(&name2User, 50).MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index c3bbce2bde5..df1949ed6d0 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -48,11 +48,11 @@ func main() { // * [nt](/r/demo/users:nt) // * [satoshi](/r/demo/users:satoshi) // * [sys](/r/demo/users:sys) -// **1** | [2](?page=2) -// ======================================== // * [test1](/r/demo/users:test1) // * [x](/r/demo/users:x) -// [1](?page=1) | **2** +// +// ======================================== +// // ======================================== // ## user gnouser // From ec2687b543cd33e98794ef84913b7506d08350e8 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 30 Sep 2024 20:08:09 +0200 Subject: [PATCH 24/71] add cmts --- examples/gno.land/r/gnoland/hof/errors.gno | 1 + examples/gno.land/r/gnoland/hof/hof.gno | 111 ++++++++---------- examples/gno.land/r/gnoland/hof/rendering.gno | 49 ++++++++ examples/gno.land/r/leon/cmts/cmts.gno | 28 +++++ examples/gno.land/r/leon/cmts/gno.mod | 1 + 5 files changed, 128 insertions(+), 62 deletions(-) create mode 100644 examples/gno.land/r/gnoland/hof/rendering.gno create mode 100644 examples/gno.land/r/leon/cmts/cmts.gno create mode 100644 examples/gno.land/r/leon/cmts/gno.mod diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index 4847b5a19cd..9ca22a2c7d3 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -5,6 +5,7 @@ import "errors" var ( ErrNoSuchItem = errors.New("hof: no such item exists") ErrNoSuchExhibition = errors.New("hof: no such exhibition exists") + ErrInvalidTitle = errors.New("hof: invalid title") ErrDoubleUpvote = errors.New("hof: cannot upvote twice") ErrDoubleDownvote = errors.New("hof: cannot downvote twice") ) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index ece4f4b65df..d2ef85615ad 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -6,20 +6,21 @@ package hof import ( "gno.land/p/demo/avl" "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" "std" + "strings" "time" ) var ( exhibCounter seqid.ID permanent *Exhibition + temporary *avl.Tree // id > *Exhibition ) type ( Exhibition struct { - id seqid.ID // fixed - itemCounter seqid.ID // + id seqid.ID // fixed per exhibition + itemCounter seqid.ID title string description string startTime *time.Time // given in RFC3339 @@ -43,8 +44,11 @@ func init() { title: "Permanent Exhibition", items: avl.NewTree(), } + + temporary = avl.NewTree() } +// Register registers your realm to the permanent exhibition func Register() { submission := std.PrevRealm() pkgpath := submission.PkgPath() @@ -67,73 +71,56 @@ func Register() { }) } -func RegisterFor(exhibID string) { - submission := std.PrevRealm() - pkgpath := submission.PkgPath() +// CreateTemporaryExhibition creates a temporary exhibition to be displayed while it's active +func CreateTemporaryExhibition(title, description string, start, end *time.Time) error { + // todo add admin checks - // must not exist and must be called from code - if permanent.items.Has(pkgpath) || submission.IsUser() { - panic("item exists or not code call") + if strings.TrimSpace(title) == "" { + return ErrInvalidTitle } - id := permanent.itemCounter.Next() - permanent.items.Set( - id.String(), - &Item{ - id: id, - pkgpath: pkgpath, - deployer: std.GetOrigCaller(), - blockNum: std.GetHeight(), - upvote: avl.NewTree(), - downvote: avl.NewTree(), - }) -} - -func Render(_ string) string { - out := "# Hall of Fame\n\n" - - out += permanent.Render() - - // todo render temp exhibitions - - return out -} - -func (e Exhibition) Render() string { - //out := ufmt.Sprintf("## %s\n\n", e.title) - //out += ufmt.Sprintf("%s\n\n", e.description) - - out := "
\n\n" - - e.items.ReverseIterate("", "", func(key string, value interface{}) bool { - out += "
\n\n" - id, _ := seqid.FromString(key) - out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) - out += value.(*Item).Render() - out += "
" - - return false - }) + id := exhibCounter.Next() + e := &Exhibition{ + id: id, + title: title, + description: description, + items: avl.NewTree(), + startTime: start, + endTime: end, + } - out += "
\n\n" + temporary.Set(id.String(), e) - return out + return nil } -func (i Item) Render() string { - //subpath := i.pkgpath[strings.Index(i.pkgpath, "/"):] - - out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) - out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) - out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) - out += ufmt.Sprintf(` -#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibitionID=0&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibitionID=0&itemID=%s)`, i.upvote.Size(), i.id.String(), i.downvote.Size(), i.id.String()) - - return out -} +// RegisterFor registers your realm for a specific exhibition +//func RegisterFor(exhibID string) { +// submission := std.PrevRealm() +// pkgpath := submission.PkgPath() +// +// raw, ok := temporary +// +// // must not exist and must be called from code +// if permanent.items.Has(pkgpath) || submission.IsUser() { +// panic("item exists or not code call") +// } +// +// id := permanent.itemCounter.Next() +// permanent.items.Set( +// id.String(), +// &Item{ +// id: id, +// pkgpath: pkgpath, +// deployer: std.GetOrigCaller(), +// blockNum: std.GetHeight(), +// upvote: avl.NewTree(), +// downvote: avl.NewTree(), +// }) +//} func Upvote(exhibID, itemID seqid.ID) error { - if exhibID == seqid.ID(0) { + if exhibID == seqid.ID(0) { // permanent exhibition raw, ok := permanent.items.Get(itemID.String()) if !ok { return ErrNoSuchItem @@ -155,7 +142,7 @@ func Upvote(exhibID, itemID seqid.ID) error { } func Downvote(exhibID, itemID seqid.ID) error { - if exhibID == seqid.ID(0) { + if exhibID == seqid.ID(0) { // permanent exhibition raw, ok := permanent.items.Get(itemID.String()) if !ok { return ErrNoSuchItem diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno new file mode 100644 index 00000000000..4f8b6fce0b0 --- /dev/null +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -0,0 +1,49 @@ +package hof + +import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" +) + +func Render(_ string) string { + out := "# Hall of Fame\n\n" + + out += permanent.Render() + + // todo render temp exhibitions + + return out +} + +func (e Exhibition) Render() string { + out := ufmt.Sprintf("## %s\n\n", e.title) + out += ufmt.Sprintf("%s\n\n", e.description) + + out += "
\n\n" + + e.items.ReverseIterate("", "", func(key string, value interface{}) bool { + out += "
\n\n" + id, _ := seqid.FromString(key) + out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) + out += value.(*Item).Render() + out += "
" + + return false + }) + + out += "
\n\n" + + return out +} + +func (i Item) Render() string { + //subpath := i.pkgpath[strings.Index(i.pkgpath, "/"):] + + out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) + out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) + out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) + out += ufmt.Sprintf(` +#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=0&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=0&itemID=%s)`, i.upvote.Size(), i.id.String(), i.downvote.Size(), i.id.String()) + + return out +} diff --git a/examples/gno.land/r/leon/cmts/cmts.gno b/examples/gno.land/r/leon/cmts/cmts.gno new file mode 100644 index 00000000000..be7b7394f18 --- /dev/null +++ b/examples/gno.land/r/leon/cmts/cmts.gno @@ -0,0 +1,28 @@ +// Package cmts a simple, pluggable, yet configurable comment functionality set +package cmts + +import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/ownable" + "github.com/gnolang/gno/gnovm/stdlibs/std" +) + +type CommentSet struct { + owner *ownable.Ownable + set *avl.Tree +} + +type Comment struct { + text string + author std.Address + up, down int + replies *avl.Tree + deletable bool +} + +func NewCommentSet() *CommentSet { + return &CommentSet{ + owner: ownable.New(), + set: avl.NewTree(), + } +} diff --git a/examples/gno.land/r/leon/cmts/gno.mod b/examples/gno.land/r/leon/cmts/gno.mod new file mode 100644 index 00000000000..d99e4fc5c66 --- /dev/null +++ b/examples/gno.land/r/leon/cmts/gno.mod @@ -0,0 +1 @@ +module gno.land/p/leon/cmts \ No newline at end of file From 3c21976e5beeaf979515f7c2682ad39b5873d2ab Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 30 Sep 2024 23:22:07 +0200 Subject: [PATCH 25/71] cmts --- examples/gno.land/r/leon/cmts/cmts.gno | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/leon/cmts/cmts.gno b/examples/gno.land/r/leon/cmts/cmts.gno index be7b7394f18..8e89fe58c27 100644 --- a/examples/gno.land/r/leon/cmts/cmts.gno +++ b/examples/gno.land/r/leon/cmts/cmts.gno @@ -8,8 +8,9 @@ import ( ) type CommentSet struct { - owner *ownable.Ownable - set *avl.Tree + owner *ownable.Ownable + set *avl.Tree + idCounter *avl.Tree } type Comment struct { @@ -26,3 +27,11 @@ func NewCommentSet() *CommentSet { set: avl.NewTree(), } } + +// NewCommentSetWithOwnable can be used to set the same ownable to multiple items +func NewCommentSetWithOwnable(o *ownable.Ownable) *CommentSet { + return &CommentSet{ + owner: o, + set: avl.NewTree(), + } +} From fa11b611fcc4c0f82ea4008f96e3bba705e80890 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 30 Sep 2024 23:30:53 +0200 Subject: [PATCH 26/71] start cs funcs --- examples/gno.land/r/leon/cmts/cmts.gno | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/leon/cmts/cmts.gno b/examples/gno.land/r/leon/cmts/cmts.gno index 8e89fe58c27..f21132fe86b 100644 --- a/examples/gno.land/r/leon/cmts/cmts.gno +++ b/examples/gno.land/r/leon/cmts/cmts.gno @@ -28,10 +28,14 @@ func NewCommentSet() *CommentSet { } } -// NewCommentSetWithOwnable can be used to set the same ownable to multiple items +// NewCommentSetWithOwnable can be used to set the same ownable to make multiple sets func NewCommentSetWithOwnable(o *ownable.Ownable) *CommentSet { return &CommentSet{ owner: o, set: avl.NewTree(), } } + +func (cs *CommentSet) AddComment() { + +} From 48ebe1ac8dd0e15a835fde8f3982b30c04549018 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 11:57:39 +0200 Subject: [PATCH 27/71] rm cmts --- examples/gno.land/r/leon/cmts/cmts.gno | 41 -------------------------- examples/gno.land/r/leon/cmts/gno.mod | 1 - 2 files changed, 42 deletions(-) delete mode 100644 examples/gno.land/r/leon/cmts/cmts.gno delete mode 100644 examples/gno.land/r/leon/cmts/gno.mod diff --git a/examples/gno.land/r/leon/cmts/cmts.gno b/examples/gno.land/r/leon/cmts/cmts.gno deleted file mode 100644 index f21132fe86b..00000000000 --- a/examples/gno.land/r/leon/cmts/cmts.gno +++ /dev/null @@ -1,41 +0,0 @@ -// Package cmts a simple, pluggable, yet configurable comment functionality set -package cmts - -import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/avl" - "github.com/gnolang/gno/examples/gno.land/p/demo/ownable" - "github.com/gnolang/gno/gnovm/stdlibs/std" -) - -type CommentSet struct { - owner *ownable.Ownable - set *avl.Tree - idCounter *avl.Tree -} - -type Comment struct { - text string - author std.Address - up, down int - replies *avl.Tree - deletable bool -} - -func NewCommentSet() *CommentSet { - return &CommentSet{ - owner: ownable.New(), - set: avl.NewTree(), - } -} - -// NewCommentSetWithOwnable can be used to set the same ownable to make multiple sets -func NewCommentSetWithOwnable(o *ownable.Ownable) *CommentSet { - return &CommentSet{ - owner: o, - set: avl.NewTree(), - } -} - -func (cs *CommentSet) AddComment() { - -} diff --git a/examples/gno.land/r/leon/cmts/gno.mod b/examples/gno.land/r/leon/cmts/gno.mod deleted file mode 100644 index d99e4fc5c66..00000000000 --- a/examples/gno.land/r/leon/cmts/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/p/leon/cmts \ No newline at end of file From 5a5c1b50caf18749f2ad54d27f694e4f0103f5b4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 12:38:42 +0200 Subject: [PATCH 28/71] add widget --- examples/gno.land/p/demo/fqname/fqname.gno | 3 ++ examples/gno.land/r/gnoland/hof/errors.gno | 7 +++- examples/gno.land/r/gnoland/hof/hof.gno | 7 ++-- examples/gno.land/r/gnoland/hof/rendering.gno | 33 +++++++++++++++++++ examples/gno.land/r/gnoland/home/home.gno | 11 +++++++ 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/p/demo/fqname/fqname.gno b/examples/gno.land/p/demo/fqname/fqname.gno index d28453e5c1b..ffa813d1478 100644 --- a/examples/gno.land/p/demo/fqname/fqname.gno +++ b/examples/gno.land/p/demo/fqname/fqname.gno @@ -63,10 +63,13 @@ func RenderLink(pkgPath, slug string) string { if slug != "" { return "[" + pkgPath + "](" + pkgLink + ")." + slug } + return "[" + pkgPath + "](" + pkgLink + ")" } + if slug != "" { return pkgPath + "." + slug } + return pkgPath } diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index 9ca22a2c7d3..218d45d8e55 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -1,6 +1,9 @@ package hof -import "errors" +import ( + "errors" + "strconv" +) var ( ErrNoSuchItem = errors.New("hof: no such item exists") @@ -8,4 +11,6 @@ var ( ErrInvalidTitle = errors.New("hof: invalid title") ErrDoubleUpvote = errors.New("hof: cannot upvote twice") ErrDoubleDownvote = errors.New("hof: cannot downvote twice") + ErrMinWidgetSize = errors.New("hof: widget size cannot be less than 1") + ErrMaxWidgetSize = errors.New("hof: widget size cannot be more than " + strconv.Itoa(MaxWidgetSize)) ) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index d2ef85615ad..6fbfab4bd4b 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,9 +4,9 @@ package hof import ( - "gno.land/p/demo/avl" - "gno.land/p/demo/seqid" - "std" + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/gnovm/stdlibs/std" "strings" "time" ) @@ -41,7 +41,6 @@ type ( func init() { permanent = &Exhibition{ id: exhibCounter.Next(), - title: "Permanent Exhibition", items: avl.NewTree(), } diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 4f8b6fce0b0..517c8c5a1f9 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -1,10 +1,15 @@ package hof import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/fqname" "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" ) +const ( + MaxWidgetSize = 5 +) + func Render(_ string) string { out := "# Hall of Fame\n\n" @@ -15,6 +20,34 @@ func Render(_ string) string { return out } +func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) (string, error) { + if itemsToRender > MaxWidgetSize { + return "", ErrMaxWidgetSize + } + + if itemsToRender < 1 { + return "", ErrMinWidgetSize + } + + out := "" + i := 0 + + if exhibID == seqid.ID(0) { + permanent.items.Iterate("", "", func(key string, value interface{}) bool { + item := value.(Item) + out += ufmt.Sprintf("- %s", fqname.RenderLink(item.pkgpath, "")) + + i++ + return i >= itemsToRender + }) + + return out, nil + } + + // todo render temp exhibs + return "", nil +} + func (e Exhibition) Render() string { out := ufmt.Sprintf("## %s\n\n", e.title) out += ufmt.Sprintf("%s\n\n", e.description) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 921492d81b4..7516f0f3699 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -1,6 +1,8 @@ package home import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/examples/gno.land/r/gnoland/hof" "std" "gno.land/p/demo/ownable" @@ -90,6 +92,15 @@ func upcomingEvents() ui.Element { } } +func latestHOFItems() ui.Element { + submissions, _ := hof.RenderExhibWidget(seqid.ID(0), 5) + + return ui.Element{ + ui.H3("[Hall of Fame](/r/gnoland/hof)"), + ui.Text(submissions), + } +} + func introSection() ui.Element { return ui.Element{ ui.H3("We’re building gno.land, set to become the leading open-source smart contract platform, using Gno, an interpreted and fully deterministic variation of the Go programming language for succinct and composable smart contracts."), From 2cef5df4ede832cf1952b9805708d1df7dcb2a2e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 13:31:19 +0200 Subject: [PATCH 29/71] add temp exhib --- examples/gno.land/r/gnoland/hof/errors.gno | 1 + examples/gno.land/r/gnoland/hof/gno.mod | 1 + examples/gno.land/r/gnoland/hof/hof.gno | 152 +++++++++--------- examples/gno.land/r/gnoland/hof/rendering.gno | 26 +-- 4 files changed, 88 insertions(+), 92 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index 218d45d8e55..c3667a2964b 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -13,4 +13,5 @@ var ( ErrDoubleDownvote = errors.New("hof: cannot downvote twice") ErrMinWidgetSize = errors.New("hof: widget size cannot be less than 1") ErrMaxWidgetSize = errors.New("hof: widget size cannot be more than " + strconv.Itoa(MaxWidgetSize)) + ErrNonCodeCall = errors.New("hof: non-code call") ) diff --git a/examples/gno.land/r/gnoland/hof/gno.mod b/examples/gno.land/r/gnoland/hof/gno.mod index 8950dfedf09..cd4d1168523 100644 --- a/examples/gno.land/r/gnoland/hof/gno.mod +++ b/examples/gno.land/r/gnoland/hof/gno.mod @@ -2,6 +2,7 @@ module gno.land/r/gnoland/hof require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/fqname 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/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 6fbfab4bd4b..90ebd52ff43 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -6,6 +6,7 @@ package hof import ( "github.com/gnolang/gno/examples/gno.land/p/demo/avl" "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" "github.com/gnolang/gno/gnovm/stdlibs/std" "strings" "time" @@ -13,8 +14,7 @@ import ( var ( exhibCounter seqid.ID - permanent *Exhibition - temporary *avl.Tree // id > *Exhibition + exhibitions *avl.Tree // id > *Exhibition ) type ( @@ -39,34 +39,12 @@ type ( ) func init() { - permanent = &Exhibition{ - id: exhibCounter.Next(), - items: avl.NewTree(), - } - - temporary = avl.NewTree() -} - -// Register registers your realm to the permanent exhibition -func Register() { - submission := std.PrevRealm() - pkgpath := submission.PkgPath() + exhibitions = avl.NewTree() - // must not exist and must be called from code - if permanent.items.Has(pkgpath) || submission.IsUser() { - panic("item exists or not code call") - } - - id := permanent.itemCounter.Next() - permanent.items.Set( - id.String(), - &Item{ - id: id, - pkgpath: pkgpath, - deployer: std.GetOrigCaller(), - blockNum: std.GetHeight(), - upvote: avl.NewTree(), - downvote: avl.NewTree(), + exhibitions.Set(exhibCounter.Next().String(), + &Exhibition{ // permanent exhibition, ID=0 + id: exhibCounter.Next(), + items: avl.NewTree(), }) } @@ -88,76 +66,92 @@ func CreateTemporaryExhibition(title, description string, start, end *time.Time) endTime: end, } - temporary.Set(id.String(), e) + exhibitions.Set(id.String(), e) return nil } +// Register registers to the permanent exhibition +func Register() (string, error) { + return RegisterFor(seqid.ID(0)) +} + // RegisterFor registers your realm for a specific exhibition -//func RegisterFor(exhibID string) { -// submission := std.PrevRealm() -// pkgpath := submission.PkgPath() -// -// raw, ok := temporary -// -// // must not exist and must be called from code -// if permanent.items.Has(pkgpath) || submission.IsUser() { -// panic("item exists or not code call") -// } -// -// id := permanent.itemCounter.Next() -// permanent.items.Set( -// id.String(), -// &Item{ -// id: id, -// pkgpath: pkgpath, -// deployer: std.GetOrigCaller(), -// blockNum: std.GetHeight(), -// upvote: avl.NewTree(), -// downvote: avl.NewTree(), -// }) -//} +func RegisterFor(exhibID seqid.ID) (string, error) { + submission := std.PrevRealm() + pkgpath := submission.PkgPath() + + raw, ok := exhibitions.Get(exhibID.String()) + if !ok { + return "", ErrNoSuchExhibition + } + + e := raw.(*Exhibition) + + // Must not already exist and must be called from code + if e.items.Has(pkgpath) || submission.IsUser() { + return "", ErrNonCodeCall + } + + id := e.itemCounter.Next() + e.items.Set( + id.String(), + &Item{ + id: id, + pkgpath: pkgpath, + deployer: std.GetOrigCaller(), + blockNum: std.GetHeight(), + upvote: avl.NewTree(), + downvote: avl.NewTree(), + }) + + return ufmt.Sprintf("Registered %s at ID %s for exhibition %s", pkgpath, id.String(), e.title), nil +} func Upvote(exhibID, itemID seqid.ID) error { - if exhibID == seqid.ID(0) { // permanent exhibition - raw, ok := permanent.items.Get(itemID.String()) - if !ok { - return ErrNoSuchItem - } + raw, ok := exhibitions.Get(exhibID.String()) + if !ok { + return ErrNoSuchExhibition + } - item := raw.(*Item) - caller := std.PrevRealm().Addr().String() + e := raw.(*Exhibition) - if item.upvote.Has(caller) { - return ErrDoubleUpvote - } + rawItem, ok := e.items.Get(itemID.String()) + if !ok { + return ErrNoSuchItem + } + + item := rawItem.(*Item) + caller := std.PrevRealm().Addr().String() - item.upvote.Set(caller, struct{}{}) - return nil + if item.upvote.Has(caller) { + return ErrDoubleUpvote } - // todo add temp exh + item.upvote.Set(caller, struct{}{}) return nil } func Downvote(exhibID, itemID seqid.ID) error { - if exhibID == seqid.ID(0) { // permanent exhibition - raw, ok := permanent.items.Get(itemID.String()) - if !ok { - return ErrNoSuchItem - } + raw, ok := exhibitions.Get(exhibID.String()) + if !ok { + return ErrNoSuchExhibition + } - item := raw.(*Item) - caller := std.PrevRealm().Addr().String() + e := raw.(*Exhibition) + + rawItem, ok := e.items.Get(itemID.String()) + if !ok { + return ErrNoSuchItem + } - if item.downvote.Has(caller) { - return ErrDoubleDownvote - } + item := rawItem.(*Item) + caller := std.PrevRealm().Addr().String() - item.downvote.Set(caller, struct{}{}) - return nil + if item.downvote.Has(caller) { + return ErrDoubleDownvote } - // todo add temp exh + item.downvote.Set(caller, struct{}{}) return nil } diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 517c8c5a1f9..00e6ff0e15f 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -32,20 +32,22 @@ func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) (string, error) { out := "" i := 0 - if exhibID == seqid.ID(0) { - permanent.items.Iterate("", "", func(key string, value interface{}) bool { - item := value.(Item) - out += ufmt.Sprintf("- %s", fqname.RenderLink(item.pkgpath, "")) + e, ok := exhibitions.Get(exhibID.String()) + if !ok { + return "", ErrNoSuchExhibition + } - i++ - return i >= itemsToRender - }) + e.(*Exhibition).items.Iterate("", "", func(key string, value interface{}) bool { + item := value.(*Item) - return out, nil - } + out += ufmt.Sprintf("- %s\n", fqname.RenderLink(item.pkgpath, "")) + + i++ + return i >= itemsToRender + }) + + return out, nil - // todo render temp exhibs - return "", nil } func (e Exhibition) Render() string { @@ -70,8 +72,6 @@ func (e Exhibition) Render() string { } func (i Item) Render() string { - //subpath := i.pkgpath[strings.Index(i.pkgpath, "/"):] - out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) From 54e414b9f10f5381bcc23df8feb4d8b253739e7f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 13:38:06 +0200 Subject: [PATCH 30/71] rendering --- examples/gno.land/r/gnoland/hof/hof.gno | 8 ++++---- examples/gno.land/r/gnoland/hof/rendering.gno | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 90ebd52ff43..9a27742aa56 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,10 +4,10 @@ package hof import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/avl" - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" - "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" - "github.com/gnolang/gno/gnovm/stdlibs/std" + "gno.land/p/demo/avl" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" + "std" "strings" "time" ) diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 00e6ff0e15f..6ecd382ecda 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -1,9 +1,9 @@ package hof import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/fqname" - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" - "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + "gno.land/p/demo/fqname" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" ) const ( @@ -13,9 +13,11 @@ const ( func Render(_ string) string { out := "# Hall of Fame\n\n" - out += permanent.Render() - - // todo render temp exhibitions + exhibitions.Iterate("", "", func(key string, value interface{}) bool { + e := value.(*Exhibition) + out += e.Render() + return false + }) return out } From 8f3d7964ad55757ba9412e2b74489ba449a1aafb Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 16:23:22 +0200 Subject: [PATCH 31/71] rendering --- examples/gno.land/r/gnoland/hof/hof.gno | 20 +++- examples/gno.land/r/gnoland/hof/rendering.gno | 100 +++++++++++------- 2 files changed, 81 insertions(+), 39 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 9a27742aa56..38bcfb7bf05 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -30,6 +30,7 @@ type ( Item struct { id seqid.ID + exhibID seqid.ID pkgpath string deployer std.Address blockNum int64 @@ -41,10 +42,11 @@ type ( func init() { exhibitions = avl.NewTree() - exhibitions.Set(exhibCounter.Next().String(), + exhibitions.Set(seqid.ID(0).String(), &Exhibition{ // permanent exhibition, ID=0 - id: exhibCounter.Next(), + id: seqid.ID(0), items: avl.NewTree(), + title: "Permanent Exhibition", }) } @@ -98,6 +100,7 @@ func RegisterFor(exhibID seqid.ID) (string, error) { id.String(), &Item{ id: id, + exhibID: e.id, pkgpath: pkgpath, deployer: std.GetOrigCaller(), blockNum: std.GetHeight(), @@ -155,3 +158,16 @@ func Downvote(exhibID, itemID seqid.ID) error { item.downvote.Set(caller, struct{}{}) return nil } + +func Delete(exhibID, itemID seqid.ID) error { + raw, ok := exhibitions.Get(exhibID.String()) + if !ok { + return ErrNoSuchExhibition + } + + if _, removed := raw.(*Exhibition).items.Remove(itemID.String()); !removed { + return ErrNoSuchItem + } + + return nil +} diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 6ecd382ecda..82a5d1bf856 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -8,61 +8,38 @@ import ( const ( MaxWidgetSize = 5 + likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=%s&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=%s&itemID=%s)\n\n" + delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&exhibID=%s&itemID=%s)\n\n" ) -func Render(_ string) string { +func Render(path string) string { out := "# Hall of Fame\n\n" exhibitions.Iterate("", "", func(key string, value interface{}) bool { e := value.(*Exhibition) - out += e.Render() + out += e.Render(path == "dashboard") + return false }) return out } -func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) (string, error) { - if itemsToRender > MaxWidgetSize { - return "", ErrMaxWidgetSize - } - - if itemsToRender < 1 { - return "", ErrMinWidgetSize - } - - out := "" - i := 0 - - e, ok := exhibitions.Get(exhibID.String()) - if !ok { - return "", ErrNoSuchExhibition - } - - e.(*Exhibition).items.Iterate("", "", func(key string, value interface{}) bool { - item := value.(*Item) - - out += ufmt.Sprintf("- %s\n", fqname.RenderLink(item.pkgpath, "")) - - i++ - return i >= itemsToRender - }) - - return out, nil - -} - -func (e Exhibition) Render() string { +func (e Exhibition) Render(dashboard bool) string { out := ufmt.Sprintf("## %s\n\n", e.title) out += ufmt.Sprintf("%s\n\n", e.description) + if e.items.Size() == 0 { + return "No exhibits for now." + } + out += "
\n\n" e.items.ReverseIterate("", "", func(key string, value interface{}) bool { out += "
\n\n" id, _ := seqid.FromString(key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) - out += value.(*Item).Render() + out += value.(*Item).Render(dashboard) out += "
" return false @@ -73,12 +50,61 @@ func (e Exhibition) Render() string { return out } -func (i Item) Render() string { +func (i Item) Render(dashboard bool) string { out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) - out += ufmt.Sprintf(` -#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=0&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=0&itemID=%s)`, i.upvote.Size(), i.id.String(), i.downvote.Size(), i.id.String()) + out += ufmt.Sprintf( + likesBar, + i.upvote.Size(), + i.exhibID.String(), + i.id.String(), + i.downvote.Size(), + i.exhibID.String(), + i.id.String(), + ) + + if dashboard { + out += ufmt.Sprintf( + delBar, + i.exhibID.String(), + i.id.String(), + ) + } return out } + +func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) (string, error) { + if itemsToRender > MaxWidgetSize { + return "", ErrMaxWidgetSize + } + + if itemsToRender < 1 { + return "", ErrMinWidgetSize + } + + e, ok := exhibitions.Get(exhibID.String()) + if !ok { + return "", ErrNoSuchExhibition + } + + exhib := e.(*Exhibition) + if exhib.items.Size() == 0 { + return "No items in this exhibition.", nil + } + + out := "" + i := 0 + + exhib.items.Iterate("", "", func(key string, value interface{}) bool { + item := value.(*Item) + + out += ufmt.Sprintf("- %s\n", fqname.RenderLink(item.pkgpath, "")) + + i++ + return i >= itemsToRender + }) + + return out, nil +} From 4a6dad6e1b0eeebdeb5437ae05b62937e58d6788 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 16:28:02 +0200 Subject: [PATCH 32/71] rm blog register --- examples/gno.land/r/gnoland/hof/hof.gno | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 38bcfb7bf05..305792eadb6 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,12 +4,13 @@ package hof import ( - "gno.land/p/demo/avl" - "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" "std" "strings" "time" + + "gno.land/p/demo/avl" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" ) var ( From 5dc3d0fe279212405e2a40bf31c8c10169d6f860 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 16:30:37 +0200 Subject: [PATCH 33/71] save --- examples/gno.land/r/gnoland/blog/admin.gno | 2 -- examples/gno.land/r/gnoland/home/gno.mod | 2 ++ examples/gno.land/r/gnoland/home/home.gno | 10 +++++----- examples/gno.land/r/leon/home/home.gno | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/gnoland/blog/admin.gno b/examples/gno.land/r/gnoland/blog/admin.gno index 58e6a0a3f2c..08b0911cf24 100644 --- a/examples/gno.land/r/gnoland/blog/admin.gno +++ b/examples/gno.land/r/gnoland/blog/admin.gno @@ -7,7 +7,6 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/context" "gno.land/p/gov/proposal" - "gno.land/r/gnoland/hof" ) var ( @@ -20,7 +19,6 @@ var ( func init() { // adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis. adminAddr = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" - hof.Register() } func AdminSetAdminAddr(addr std.Address) { diff --git a/examples/gno.land/r/gnoland/home/gno.mod b/examples/gno.land/r/gnoland/home/gno.mod index c208ad421c9..708deb8654d 100644 --- a/examples/gno.land/r/gnoland/home/gno.mod +++ b/examples/gno.land/r/gnoland/home/gno.mod @@ -2,8 +2,10 @@ module gno.land/r/gnoland/home require ( gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/ui v0.0.0-latest gno.land/r/gnoland/blog v0.0.0-latest gno.land/r/gnoland/events v0.0.0-latest + gno.land/r/gnoland/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 7516f0f3699..99d65f9a425 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -1,15 +1,15 @@ package home import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" - "github.com/gnolang/gno/examples/gno.land/r/gnoland/hof" "std" "gno.land/p/demo/ownable" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" blog "gno.land/r/gnoland/blog" events "gno.land/r/gnoland/events" + "gno.land/r/gnoland/hof" ) // XXX: p/demo/ui API is crappy, we need to make it more idiomatic @@ -39,7 +39,7 @@ func Render(_ string) string { ui.Columns{3, []ui.Element{ lastBlogposts(4), upcomingEvents(), - lastContributions(4), + latestHOFItems(5), }}, ) @@ -92,8 +92,8 @@ func upcomingEvents() ui.Element { } } -func latestHOFItems() ui.Element { - submissions, _ := hof.RenderExhibWidget(seqid.ID(0), 5) +func latestHOFItems(num int) ui.Element { + submissions, _ := hof.RenderExhibWidget(seqid.ID(0), num) return ui.Element{ ui.H3("[Hall of Fame](/r/gnoland/hof)"), diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index 7216ac02aaf..b32f0f99693 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -33,7 +33,7 @@ TODO import r/gh `, } - hof.Register() + _, _ = hof.Register() } func UpdatePFP(url, caption string) { From f0ce5de85259a95943f378e1c50ef7137544e675 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 1 Oct 2024 19:25:09 +0200 Subject: [PATCH 34/71] save --- examples/gno.land/r/gnoland/hof/rendering.gno | 15 +++++++++++++-- examples/gno.land/r/leon/home/home.gno | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 82a5d1bf856..047359562b8 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -15,10 +15,19 @@ const ( func Render(path string) string { out := "# Hall of Fame\n\n" - exhibitions.Iterate("", "", func(key string, value interface{}) bool { + // Render permanent exhib + permanent, _ := exhibitions.Get(seqid.ID(0).String()) + out += permanent.(*Exhibition).Render(path == "dashboard") + + if exhibitions.Size() > 1 { + out += "---\n\n" + } + + exhibitions.Iterate(seqid.ID(1).String(), "", func(key string, value interface{}) bool { e := value.(*Exhibition) out += e.Render(path == "dashboard") + out += "---\n\n" return false }) @@ -28,9 +37,11 @@ func Render(path string) string { func (e Exhibition) Render(dashboard bool) string { out := ufmt.Sprintf("## %s\n\n", e.title) out += ufmt.Sprintf("%s\n\n", e.description) + out += ufmt.Sprintf("Exhibition ID: %s\n\n", e.id.String()) if e.items.Size() == 0 { - return "No exhibits for now." + out += "No items in this exhibition currently.\n\n" + return out } out += "
\n\n" diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index b32f0f99693..d60cf35c76f 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -4,6 +4,7 @@ import ( "std" "strconv" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" "gno.land/r/demo/art/gnoface" @@ -33,7 +34,7 @@ TODO import r/gh `, } - _, _ = hof.Register() + _, _ = hof.RegisterFor(seqid.ID(1)) } func UpdatePFP(url, caption string) { From 296f7d8131e8bbc99da4127103304ae52be3cd6f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 4 Oct 2024 15:22:30 +0200 Subject: [PATCH 35/71] rm errs --- examples/gno.land/r/gnoland/hof/errors.gno | 1 + examples/gno.land/r/gnoland/hof/hof.gno | 23 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index c3667a2964b..66336c409c0 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -7,6 +7,7 @@ import ( var ( ErrNoSuchItem = errors.New("hof: no such item exists") + ErrAlreadyExists = errors.New("hof: alerady exists") ErrNoSuchExhibition = errors.New("hof: no such exhibition exists") ErrInvalidTitle = errors.New("hof: invalid title") ErrDoubleUpvote = errors.New("hof: cannot upvote twice") diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 305792eadb6..8524e78e595 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -10,7 +10,6 @@ import ( "gno.land/p/demo/avl" "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" ) var ( @@ -51,8 +50,8 @@ func init() { }) } -// CreateTemporaryExhibition creates a temporary exhibition to be displayed while it's active -func CreateTemporaryExhibition(title, description string, start, end *time.Time) error { +// CreateExhibition creates a temporary exhibition to be displayed while it's active +func CreateExhibition(title, description string, start, end *time.Time) error { // todo add admin checks if strings.TrimSpace(title) == "" { @@ -75,25 +74,29 @@ func CreateTemporaryExhibition(title, description string, start, end *time.Time) } // Register registers to the permanent exhibition -func Register() (string, error) { - return RegisterFor(seqid.ID(0)) +func Register() { + RegisterFor(seqid.ID(0)) } // RegisterFor registers your realm for a specific exhibition -func RegisterFor(exhibID seqid.ID) (string, error) { +func RegisterFor(exhibID seqid.ID) { submission := std.PrevRealm() pkgpath := submission.PkgPath() raw, ok := exhibitions.Get(exhibID.String()) if !ok { - return "", ErrNoSuchExhibition + panic(ErrNoSuchExhibition.Error()) } e := raw.(*Exhibition) // Must not already exist and must be called from code - if e.items.Has(pkgpath) || submission.IsUser() { - return "", ErrNonCodeCall + if submission.IsUser() { + panic(ErrNonCodeCall.Error()) + } + + if e.items.Has(pkgpath) { + panic(ErrAlreadyExists.Error()) } id := e.itemCounter.Next() @@ -108,8 +111,6 @@ func RegisterFor(exhibID seqid.ID) (string, error) { upvote: avl.NewTree(), downvote: avl.NewTree(), }) - - return ufmt.Sprintf("Registered %s at ID %s for exhibition %s", pkgpath, id.String(), e.title), nil } func Upvote(exhibID, itemID seqid.ID) error { From 1c107329eaf5913e6bd9006ca9bf800d12bfe25b Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:51:45 +0200 Subject: [PATCH 36/71] Update examples/gno.land/p/demo/avl/pager/pager.gno Co-authored-by: Antonio Navarro Perez --- examples/gno.land/p/demo/avl/pager/pager.gno | 42 +++++++------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index fb2e979c51b..6c9c23d8e3b 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -54,43 +54,31 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) - if pageSize < 1 { - return &Page{ - Items: []Item{}, - PageNumber: 0, - PageSize: 0, + page := &Page{ TotalItems: totalItems, TotalPages: totalPages, - HasPrev: false, - HasNext: false, + PageSize: pageSize, Pager: p, } + + // pages without content + if pageSize < 1 { + return page } + // page number provided is not available if pageNumber < 1 { - return &Page{ - Items: []Item{}, - PageNumber: 0, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: false, - HasNext: pageNumber < totalPages, - Pager: p, - } + page.HasNext = pageNumber < totalPages + + return page } + // page number provided is outside the range of total pages if pageNumber > totalPages { - return &Page{ - Items: []Item{}, - PageNumber: pageNumber, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: totalPages > 0, - HasNext: false, - Pager: p, - } + page.PageNumber = pageNumber + page.HasPrev = totalPages > 0 + + return page } startIndex := (pageNumber - 1) * pageSize From 79ffd7db24c480be1f0cdf2ea44b0c8d106a6073 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:56:38 +0200 Subject: [PATCH 37/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 31 ++++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 6c9c23d8e3b..5e79aac8458 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -55,11 +55,11 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) page := &Page{ - TotalItems: totalItems, - TotalPages: totalPages, - PageSize: pageSize, - Pager: p, - } + TotalItems: totalItems, + TotalPages: totalPages, + PageSize: pageSize, + Pager: p, + } // pages without content if pageSize < 1 { @@ -68,16 +68,14 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { // page number provided is not available if pageNumber < 1 { - page.HasNext = pageNumber < totalPages - + page.HasNext = totalPages > 0 return page } // page number provided is outside the range of total pages if pageNumber > totalPages { page.PageNumber = pageNumber - page.HasPrev = totalPages > 0 - + page.HasPrev = pageNumber > 0 return page } @@ -93,16 +91,11 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { return false }) - return &Page{ - Items: items, - PageNumber: pageNumber, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: pageNumber > 1, - HasNext: pageNumber < totalPages, - Pager: p, - } + page.Items = items + page.PageNumber = pageNumber + page.HasPrev = pageNumber > 1 + page.HasNext = pageNumber < totalPages + return page } func (p *Pager) MustGetPageByPath(rawURL string) *Page { From 3754a7093a6f658d6be474340f03e0cd44964c34 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 22:33:58 +0200 Subject: [PATCH 38/71] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 5e79aac8458..60bb44d97b6 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -205,13 +205,6 @@ func (p *Pager) ParseQuery(rawURL string) (int, int, error) { return pageNumber, pageSize, nil } -func min(a, b int) int { - if a < b { - return a - } - return b -} - func max(a, b int) int { if a > b { return a From 19f7fa93995e3ca2352977708df3c38d2252bf7f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 16:00:31 +0200 Subject: [PATCH 39/71] rm temp exhib --- examples/gno.land/r/gnoland/hof/hof.gno | 97 ++++--------------------- 1 file changed, 15 insertions(+), 82 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 8524e78e595..0902eddfa97 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -5,32 +5,23 @@ package hof import ( "std" - "strings" - "time" "gno.land/p/demo/avl" "gno.land/p/demo/seqid" ) -var ( - exhibCounter seqid.ID - exhibitions *avl.Tree // id > *Exhibition -) +var exhibition *Exhibition type ( Exhibition struct { - id seqid.ID // fixed per exhibition itemCounter seqid.ID title string description string - startTime *time.Time // given in RFC3339 - endTime *time.Time // end time of the event, given in RFC3339 - items *avl.Tree // Item id > Item + items *avl.Tree // Item id > Item } Item struct { id seqid.ID - exhibID seqid.ID pkgpath string deployer std.Address blockNum int64 @@ -40,71 +31,31 @@ type ( ) func init() { - exhibitions = avl.NewTree() - - exhibitions.Set(seqid.ID(0).String(), - &Exhibition{ // permanent exhibition, ID=0 - id: seqid.ID(0), - items: avl.NewTree(), - title: "Permanent Exhibition", - }) -} - -// CreateExhibition creates a temporary exhibition to be displayed while it's active -func CreateExhibition(title, description string, start, end *time.Time) error { - // todo add admin checks - - if strings.TrimSpace(title) == "" { - return ErrInvalidTitle - } - - id := exhibCounter.Next() - e := &Exhibition{ - id: id, - title: title, - description: description, - items: avl.NewTree(), - startTime: start, - endTime: end, + exhibition = &Exhibition{ + items: avl.NewTree(), + title: "Hall of Fame", } - - exhibitions.Set(id.String(), e) - - return nil } -// Register registers to the permanent exhibition +// Register registers your realm for a specific exhibition func Register() { - RegisterFor(seqid.ID(0)) -} - -// RegisterFor registers your realm for a specific exhibition -func RegisterFor(exhibID seqid.ID) { submission := std.PrevRealm() pkgpath := submission.PkgPath() - raw, ok := exhibitions.Get(exhibID.String()) - if !ok { - panic(ErrNoSuchExhibition.Error()) - } - - e := raw.(*Exhibition) - // Must not already exist and must be called from code if submission.IsUser() { panic(ErrNonCodeCall.Error()) } - if e.items.Has(pkgpath) { + if exhibition.items.Has(pkgpath) { panic(ErrAlreadyExists.Error()) } - id := e.itemCounter.Next() - e.items.Set( + id := exhibition.itemCounter.Next() + exhibition.items.Set( id.String(), &Item{ id: id, - exhibID: e.id, pkgpath: pkgpath, deployer: std.GetOrigCaller(), blockNum: std.GetHeight(), @@ -113,15 +64,8 @@ func RegisterFor(exhibID seqid.ID) { }) } -func Upvote(exhibID, itemID seqid.ID) error { - raw, ok := exhibitions.Get(exhibID.String()) - if !ok { - return ErrNoSuchExhibition - } - - e := raw.(*Exhibition) - - rawItem, ok := e.items.Get(itemID.String()) +func Upvote(itemID seqid.ID) error { + rawItem, ok := exhibition.items.Get(itemID.String()) if !ok { return ErrNoSuchItem } @@ -137,15 +81,9 @@ func Upvote(exhibID, itemID seqid.ID) error { return nil } -func Downvote(exhibID, itemID seqid.ID) error { - raw, ok := exhibitions.Get(exhibID.String()) - if !ok { - return ErrNoSuchExhibition - } - - e := raw.(*Exhibition) +func Downvote(itemID seqid.ID) error { - rawItem, ok := e.items.Get(itemID.String()) + rawItem, ok := exhibition.items.Get(itemID.String()) if !ok { return ErrNoSuchItem } @@ -161,13 +99,8 @@ func Downvote(exhibID, itemID seqid.ID) error { return nil } -func Delete(exhibID, itemID seqid.ID) error { - raw, ok := exhibitions.Get(exhibID.String()) - if !ok { - return ErrNoSuchExhibition - } - - if _, removed := raw.(*Exhibition).items.Remove(itemID.String()); !removed { +func Delete(itemID seqid.ID) error { + if _, removed := exhibition.items.Remove(itemID.String()); !removed { return ErrNoSuchItem } From a64651897956d45980086aa2f9cd054f0a50d6ea Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 16:34:00 +0200 Subject: [PATCH 40/71] add pausable --- .../gno.land/p/demo/pausable/pausable.gno | 10 +++++- examples/gno.land/r/gnoland/hof/hof.gno | 24 ++++++++++---- examples/gno.land/r/gnoland/hof/rendering.gno | 33 ++++--------------- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index eae3456ba61..214642ab2f3 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -1,6 +1,10 @@ package pausable -import "gno.land/p/demo/ownable" +import ( + "std" + + "gno.land/p/demo/ownable" +) type Pausable struct { *ownable.Ownable @@ -35,6 +39,8 @@ func (p *Pausable) Pause() error { } p.paused = true + std.Emit("Paused", "account", p.GetOwner()) + return nil } @@ -45,5 +51,7 @@ func (p *Pausable) Unpause() error { } p.paused = false + std.Emit("Unpaused", "account", p.GetOwner()) + return nil } diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index 0902eddfa97..bb56ad8b69d 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,20 +4,25 @@ package hof import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/pausable" "std" "gno.land/p/demo/avl" + "gno.land/p/demo/ownable" "gno.land/p/demo/seqid" ) -var exhibition *Exhibition +var ( + exhibition *Exhibition + owner *ownable.Ownable +) type ( Exhibition struct { itemCounter seqid.ID - title string description string items *avl.Tree // Item id > Item + *pausable.Pausable } Item struct { @@ -33,16 +38,23 @@ type ( func init() { exhibition = &Exhibition{ items: avl.NewTree(), - title: "Hall of Fame", } + + owner = ownable.NewWithAddress(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")) + exhibition.Pausable = pausable.NewFromOwnable(owner) } -// Register registers your realm for a specific exhibition +// Register registers your realm to the Hall of Fame +// Should be called from within code func Register() { + if exhibition.IsPaused() { + panic("hof: registering is paused") + } + submission := std.PrevRealm() pkgpath := submission.PkgPath() - // Must not already exist and must be called from code + // Must not yet exist and must be called from code if submission.IsUser() { panic(ErrNonCodeCall.Error()) } @@ -82,7 +94,6 @@ func Upvote(itemID seqid.ID) error { } func Downvote(itemID seqid.ID) error { - rawItem, ok := exhibition.items.Get(itemID.String()) if !ok { return ErrNoSuchItem @@ -100,6 +111,7 @@ func Downvote(itemID seqid.ID) error { } func Delete(itemID seqid.ID) error { + if _, removed := exhibition.items.Remove(itemID.String()); !removed { return ErrNoSuchItem } diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 047359562b8..7681170e6c3 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -7,37 +7,19 @@ import ( ) const ( - MaxWidgetSize = 5 - likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=%s&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=%s&itemID=%s)\n\n" - delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&exhibID=%s&itemID=%s)\n\n" + likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=%s&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=%s&itemID=%s)\n\n" + delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&exhibID=%s&itemID=%s)\n\n" ) func Render(path string) string { out := "# Hall of Fame\n\n" - - // Render permanent exhib - permanent, _ := exhibitions.Get(seqid.ID(0).String()) - out += permanent.(*Exhibition).Render(path == "dashboard") - - if exhibitions.Size() > 1 { - out += "---\n\n" - } - - exhibitions.Iterate(seqid.ID(1).String(), "", func(key string, value interface{}) bool { - e := value.(*Exhibition) - out += e.Render(path == "dashboard") - - out += "---\n\n" - return false - }) + out += exhibition.Render(path == "dashboard") return out } func (e Exhibition) Render(dashboard bool) string { - out := ufmt.Sprintf("## %s\n\n", e.title) - out += ufmt.Sprintf("%s\n\n", e.description) - out += ufmt.Sprintf("Exhibition ID: %s\n\n", e.id.String()) + out := ufmt.Sprintf("%s\n\n", e.description) if e.items.Size() == 0 { out += "No items in this exhibition currently.\n\n" @@ -86,13 +68,10 @@ func (i Item) Render(dashboard bool) string { return out } -func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) (string, error) { - if itemsToRender > MaxWidgetSize { - return "", ErrMaxWidgetSize - } +func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) string { if itemsToRender < 1 { - return "", ErrMinWidgetSize + return "" } e, ok := exhibitions.Get(exhibID.String()) From 94fc3614551e167a1cd2a25d15d87388e13f1c56 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 16:47:17 +0200 Subject: [PATCH 41/71] expose apis, add pausable --- .../gno.land/r/gnoland/hof/administration.gno | 22 +++++++++++++++++++ examples/gno.land/r/gnoland/hof/errors.gno | 4 ---- examples/gno.land/r/gnoland/hof/hof.gno | 5 ++++- 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 examples/gno.land/r/gnoland/hof/administration.gno diff --git a/examples/gno.land/r/gnoland/hof/administration.gno b/examples/gno.land/r/gnoland/hof/administration.gno new file mode 100644 index 00000000000..1ec1eeef443 --- /dev/null +++ b/examples/gno.land/r/gnoland/hof/administration.gno @@ -0,0 +1,22 @@ +package hof + +// Exposing the ownable & pausable APIs +// Should not be needed as soon as MsgCall supports calling methods on exported variables + +func Pause() error { + return exhibition.Pausable.Pause() +} + +func Unpause() error { + return exhibition.Pausable.Unpause() +} + +func GetOwner() std.Address { + return owner.Owner() +} + +func TransferOwnership(newOwner std.Address) { + if err := owner.TransferOwnership(newOwner); err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index 66336c409c0..4bf1a12c87a 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -2,17 +2,13 @@ package hof import ( "errors" - "strconv" ) var ( ErrNoSuchItem = errors.New("hof: no such item exists") ErrAlreadyExists = errors.New("hof: alerady exists") ErrNoSuchExhibition = errors.New("hof: no such exhibition exists") - ErrInvalidTitle = errors.New("hof: invalid title") ErrDoubleUpvote = errors.New("hof: cannot upvote twice") ErrDoubleDownvote = errors.New("hof: cannot downvote twice") - ErrMinWidgetSize = errors.New("hof: widget size cannot be less than 1") - ErrMaxWidgetSize = errors.New("hof: widget size cannot be more than " + strconv.Itoa(MaxWidgetSize)) ErrNonCodeCall = errors.New("hof: non-code call") ) diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/gnoland/hof/hof.gno index bb56ad8b69d..2c727e6509e 100644 --- a/examples/gno.land/r/gnoland/hof/hof.gno +++ b/examples/gno.land/r/gnoland/hof/hof.gno @@ -4,11 +4,11 @@ package hof import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/pausable" "std" "gno.land/p/demo/avl" "gno.land/p/demo/ownable" + "gno.land/p/demo/pausable" "gno.land/p/demo/seqid" ) @@ -111,6 +111,9 @@ func Downvote(itemID seqid.ID) error { } func Delete(itemID seqid.ID) error { + if err := owner.CallerIsOwner(); err != nil { + panic(err) + } if _, removed := exhibition.items.Remove(itemID.String()); !removed { return ErrNoSuchItem From cde9fe2c97db537a57f447f6c285f3cf5eda66e9 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 16:59:32 +0200 Subject: [PATCH 42/71] fix errors --- .../gno.land/r/gnoland/hof/administration.gno | 2 ++ examples/gno.land/r/gnoland/hof/errors.gno | 11 ++++---- examples/gno.land/r/gnoland/hof/gno.mod | 2 ++ examples/gno.land/r/gnoland/hof/rendering.gno | 26 +++++-------------- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/examples/gno.land/r/gnoland/hof/administration.gno b/examples/gno.land/r/gnoland/hof/administration.gno index 1ec1eeef443..59ce30d8c94 100644 --- a/examples/gno.land/r/gnoland/hof/administration.gno +++ b/examples/gno.land/r/gnoland/hof/administration.gno @@ -1,5 +1,7 @@ package hof +import "std" + // Exposing the ownable & pausable APIs // Should not be needed as soon as MsgCall supports calling methods on exported variables diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/gnoland/hof/errors.gno index 4bf1a12c87a..d903809fce4 100644 --- a/examples/gno.land/r/gnoland/hof/errors.gno +++ b/examples/gno.land/r/gnoland/hof/errors.gno @@ -5,10 +5,9 @@ import ( ) var ( - ErrNoSuchItem = errors.New("hof: no such item exists") - ErrAlreadyExists = errors.New("hof: alerady exists") - ErrNoSuchExhibition = errors.New("hof: no such exhibition exists") - ErrDoubleUpvote = errors.New("hof: cannot upvote twice") - ErrDoubleDownvote = errors.New("hof: cannot downvote twice") - ErrNonCodeCall = errors.New("hof: non-code call") + ErrNoSuchItem = errors.New("hof: no such item exists") + ErrAlreadyExists = errors.New("hof: alerady exists") + ErrDoubleUpvote = errors.New("hof: cannot upvote twice") + ErrDoubleDownvote = errors.New("hof: cannot downvote twice") + ErrNonCodeCall = errors.New("hof: non-code call") ) diff --git a/examples/gno.land/r/gnoland/hof/gno.mod b/examples/gno.land/r/gnoland/hof/gno.mod index cd4d1168523..140b4c5fab5 100644 --- a/examples/gno.land/r/gnoland/hof/gno.mod +++ b/examples/gno.land/r/gnoland/hof/gno.mod @@ -3,6 +3,8 @@ module gno.land/r/gnoland/hof require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/fqname v0.0.0-latest + gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/pausable 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/r/gnoland/hof/rendering.gno b/examples/gno.land/r/gnoland/hof/rendering.gno index 7681170e6c3..dd733c9a0d7 100644 --- a/examples/gno.land/r/gnoland/hof/rendering.gno +++ b/examples/gno.land/r/gnoland/hof/rendering.gno @@ -7,8 +7,8 @@ import ( ) const ( - likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&exhibID=%s&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&exhibID=%s&itemID=%s)\n\n" - delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&exhibID=%s&itemID=%s)\n\n" + likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&itemID=%s)\n\n" + delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&itemID=%s)\n\n" ) func Render(path string) string { @@ -50,17 +50,14 @@ func (i Item) Render(dashboard bool) string { out += ufmt.Sprintf( likesBar, i.upvote.Size(), - i.exhibID.String(), i.id.String(), i.downvote.Size(), - i.exhibID.String(), i.id.String(), ) if dashboard { out += ufmt.Sprintf( delBar, - i.exhibID.String(), i.id.String(), ) } @@ -68,26 +65,17 @@ func (i Item) Render(dashboard bool) string { return out } -func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) string { - +func RenderExhibWidget(itemsToRender int) string { if itemsToRender < 1 { return "" } - - e, ok := exhibitions.Get(exhibID.String()) - if !ok { - return "", ErrNoSuchExhibition - } - - exhib := e.(*Exhibition) - if exhib.items.Size() == 0 { - return "No items in this exhibition.", nil + if exhibition.items.Size() == 0 { + return "" } out := "" i := 0 - - exhib.items.Iterate("", "", func(key string, value interface{}) bool { + exhibition.items.Iterate("", "", func(key string, value interface{}) bool { item := value.(*Item) out += ufmt.Sprintf("- %s\n", fqname.RenderLink(item.pkgpath, "")) @@ -96,5 +84,5 @@ func RenderExhibWidget(exhibID seqid.ID, itemsToRender int) string { return i >= itemsToRender }) - return out, nil + return out } From 84ef0ea6a483a0ac46cb3d24eec1681e0f4bc091 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 17:05:00 +0200 Subject: [PATCH 43/71] update ownable/pausable, home, leon/home --- examples/gno.land/p/demo/ownable/ownable.gno | 2 +- examples/gno.land/p/demo/pausable/pausable.gno | 4 ++-- examples/gno.land/r/gnoland/home/home.gno | 3 +-- examples/gno.land/r/leon/home/home.gno | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index a77b22461a9..1c1f7627266 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -58,7 +58,7 @@ func (o *Ownable) DropOwnership() error { std.Emit( OwnershipTransferEvent, - "from", string(prevOwner), + "from", prevOwner.String(), "to", "", ) diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index 214642ab2f3..e9cce63c1e3 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -39,7 +39,7 @@ func (p *Pausable) Pause() error { } p.paused = true - std.Emit("Paused", "account", p.GetOwner()) + std.Emit("Paused", "account", p.Owner().String()) return nil } @@ -51,7 +51,7 @@ func (p *Pausable) Unpause() error { } p.paused = false - std.Emit("Unpaused", "account", p.GetOwner()) + std.Emit("Unpaused", "account", p.Owner().String()) return nil } diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 99d65f9a425..da9b9e20aed 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -4,7 +4,6 @@ import ( "std" "gno.land/p/demo/ownable" - "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" blog "gno.land/r/gnoland/blog" @@ -93,7 +92,7 @@ func upcomingEvents() ui.Element { } func latestHOFItems(num int) ui.Element { - submissions, _ := hof.RenderExhibWidget(seqid.ID(0), num) + submissions := hof.RenderExhibWidget(num) return ui.Element{ ui.H3("[Hall of Fame](/r/gnoland/hof)"), diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index d60cf35c76f..7216ac02aaf 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -4,7 +4,6 @@ import ( "std" "strconv" - "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" "gno.land/r/demo/art/gnoface" @@ -34,7 +33,7 @@ TODO import r/gh `, } - _, _ = hof.RegisterFor(seqid.ID(1)) + hof.Register() } func UpdatePFP(url, caption string) { From 0c9b8a67a78848ec0f934fa4904fcae745838af6 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 17:37:57 +0200 Subject: [PATCH 44/71] move --- examples/gno.land/r/{gnoland => demo}/hof/administration.gno | 0 examples/gno.land/r/{gnoland => demo}/hof/errors.gno | 0 examples/gno.land/r/{gnoland => demo}/hof/gno.mod | 0 examples/gno.land/r/{gnoland => demo}/hof/hof.gno | 0 examples/gno.land/r/{gnoland => demo}/hof/rendering.gno | 0 examples/gno.land/r/leon/home/gno.mod | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) rename examples/gno.land/r/{gnoland => demo}/hof/administration.gno (100%) rename examples/gno.land/r/{gnoland => demo}/hof/errors.gno (100%) rename examples/gno.land/r/{gnoland => demo}/hof/gno.mod (100%) rename examples/gno.land/r/{gnoland => demo}/hof/hof.gno (100%) rename examples/gno.land/r/{gnoland => demo}/hof/rendering.gno (100%) diff --git a/examples/gno.land/r/gnoland/hof/administration.gno b/examples/gno.land/r/demo/hof/administration.gno similarity index 100% rename from examples/gno.land/r/gnoland/hof/administration.gno rename to examples/gno.land/r/demo/hof/administration.gno diff --git a/examples/gno.land/r/gnoland/hof/errors.gno b/examples/gno.land/r/demo/hof/errors.gno similarity index 100% rename from examples/gno.land/r/gnoland/hof/errors.gno rename to examples/gno.land/r/demo/hof/errors.gno diff --git a/examples/gno.land/r/gnoland/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod similarity index 100% rename from examples/gno.land/r/gnoland/hof/gno.mod rename to examples/gno.land/r/demo/hof/gno.mod diff --git a/examples/gno.land/r/gnoland/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno similarity index 100% rename from examples/gno.land/r/gnoland/hof/hof.gno rename to examples/gno.land/r/demo/hof/hof.gno diff --git a/examples/gno.land/r/gnoland/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno similarity index 100% rename from examples/gno.land/r/gnoland/hof/rendering.gno rename to examples/gno.land/r/demo/hof/rendering.gno diff --git a/examples/gno.land/r/leon/home/gno.mod b/examples/gno.land/r/leon/home/gno.mod index 99e7a48aa90..4649cf4abe6 100644 --- a/examples/gno.land/r/leon/home/gno.mod +++ b/examples/gno.land/r/leon/home/gno.mod @@ -4,6 +4,6 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/r/demo/art/gnoface v0.0.0-latest gno.land/r/demo/art/millipede v0.0.0-latest - gno.land/r/gnoland/hof v0.0.0-latest + gno.land/r/demo/hof v0.0.0-latest gno.land/r/leon/config v0.0.0-latest ) From d1d60edaf278160d4f1d151bc715e1fb31cfcf7d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 18:27:43 +0200 Subject: [PATCH 45/71] add shortlink --- examples/gno.land/p/demo/fqname/fqname.gno | 17 ++++++++++++++++- .../gno.land/p/demo/fqname/fqname_test.gno | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/fqname/fqname.gno b/examples/gno.land/p/demo/fqname/fqname.gno index ffa813d1478..365a6dd740c 100644 --- a/examples/gno.land/p/demo/fqname/fqname.gno +++ b/examples/gno.land/p/demo/fqname/fqname.gno @@ -4,7 +4,9 @@ // package-level declaration. package fqname -import "strings" +import ( + "strings" +) // Parse splits a fully qualified identifier into its package path and name // components. It handles cases with and without slashes in the package path. @@ -73,3 +75,16 @@ func RenderLink(pkgPath, slug string) string { return pkgPath } + +// ShortPath returns the part of the pkgpath after the base domain. +// sp := fqname.ShortPath("gno.land/r/gnoland/home") +// fmt.Println(sp) +// // Output: /r/gnoland/home +func ShortPath(pkgPath string) string { + idx := strings.Index(pkgPath, "/") + if idx == -1 { + return "" + } + + return pkgPath[idx:] +} diff --git a/examples/gno.land/p/demo/fqname/fqname_test.gno b/examples/gno.land/p/demo/fqname/fqname_test.gno index 55a220776be..8e1829482f4 100644 --- a/examples/gno.land/p/demo/fqname/fqname_test.gno +++ b/examples/gno.land/p/demo/fqname/fqname_test.gno @@ -72,3 +72,22 @@ func TestRenderLink(t *testing.T) { uassert.Equal(t, tt.expected, result, "Rendered link did not match expected") } } + +func TestShortPath(t *testing.T) { + tests := []struct { + pkgPath string + expected string + }{ + {"gno.land/p/demo/avl", "/p/demo/avl"}, + {"github.com/a/b", "/a/b"}, + {"example.com/pkg", "/pkg"}, + {"gno.land/r/demo/foo20", "/r/demo/foo20"}, + {"gno.land/r/demo/foo20", "/r/demo/foo20"}, + {"", ""}, + } + + for _, tt := range tests { + result := ShortPath(tt.pkgPath) + uassert.Equal(t, tt.expected, result, "Rendered link did not match expected") + } +} From f6d07a9ccc656e791691933e7b2924811e42d092 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 18:30:46 +0200 Subject: [PATCH 46/71] add shortpath, fix --- examples/gno.land/r/demo/hof/gno.mod | 2 +- examples/gno.land/r/demo/hof/rendering.gno | 5 +++-- examples/gno.land/r/gnoland/blog/gno.mod | 1 - examples/gno.land/r/gnoland/home/gno.mod | 3 +-- examples/gno.land/r/gnoland/home/home.gno | 4 ++-- examples/gno.land/r/leon/home/home.gno | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/demo/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod index 140b4c5fab5..ec5c5660311 100644 --- a/examples/gno.land/r/demo/hof/gno.mod +++ b/examples/gno.land/r/demo/hof/gno.mod @@ -1,4 +1,4 @@ -module gno.land/r/gnoland/hof +module gno.land/r/demo/hof require ( gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index dd733c9a0d7..fcb54d01089 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -7,8 +7,8 @@ import ( ) const ( - likesBar = "#### [%d 👍](/r/gnoland/hof?help&__func=Upvote&itemID=%s) - [%d 👎](/r/gnoland/hof?help&__func=Downvote&itemID=%s)\n\n" - delBar = "[Delete](/r/gnoland/hof?help&__func=Delete&itemID=%s)\n\n" + likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&itemID=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&itemID=%s)\n\n" + delBar = "[Delete](/r/demo/hof?help&__func=Delete&itemID=%s)\n\n" ) func Render(path string) string { @@ -46,6 +46,7 @@ func (e Exhibition) Render(dashboard bool) string { func (i Item) Render(dashboard bool) string { out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) + out += ufmt.Sprintf("[View realm](%s)\n\n", fqname.ShortPath(i.pkgpath)) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) out += ufmt.Sprintf( likesBar, diff --git a/examples/gno.land/r/gnoland/blog/gno.mod b/examples/gno.land/r/gnoland/blog/gno.mod index 069a6970cd7..17c17e0cfa6 100644 --- a/examples/gno.land/r/gnoland/blog/gno.mod +++ b/examples/gno.land/r/gnoland/blog/gno.mod @@ -5,5 +5,4 @@ require ( gno.land/p/demo/blog v0.0.0-latest gno.land/p/demo/context v0.0.0-latest gno.land/p/gov/proposal v0.0.0-latest - gno.land/r/gnoland/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/home/gno.mod b/examples/gno.land/r/gnoland/home/gno.mod index 708deb8654d..ff52ef4c8b1 100644 --- a/examples/gno.land/r/gnoland/home/gno.mod +++ b/examples/gno.land/r/gnoland/home/gno.mod @@ -2,10 +2,9 @@ module gno.land/r/gnoland/home require ( gno.land/p/demo/ownable v0.0.0-latest - gno.land/p/demo/seqid v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/ui v0.0.0-latest + gno.land/r/demo/hof v0.0.0-latest gno.land/r/gnoland/blog v0.0.0-latest gno.land/r/gnoland/events v0.0.0-latest - gno.land/r/gnoland/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index da9b9e20aed..8405a14a126 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -6,9 +6,9 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" + "gno.land/r/demo/hof" blog "gno.land/r/gnoland/blog" events "gno.land/r/gnoland/events" - "gno.land/r/gnoland/hof" ) // XXX: p/demo/ui API is crappy, we need to make it more idiomatic @@ -95,7 +95,7 @@ func latestHOFItems(num int) ui.Element { submissions := hof.RenderExhibWidget(num) return ui.Element{ - ui.H3("[Hall of Fame](/r/gnoland/hof)"), + ui.H3("[Hall of Fame](/r/demo/hof)"), ui.Text(submissions), } } diff --git a/examples/gno.land/r/leon/home/home.gno b/examples/gno.land/r/leon/home/home.gno index 7216ac02aaf..0e37b7c4d93 100644 --- a/examples/gno.land/r/leon/home/home.gno +++ b/examples/gno.land/r/leon/home/home.gno @@ -8,7 +8,7 @@ import ( "gno.land/r/demo/art/gnoface" "gno.land/r/demo/art/millipede" - "gno.land/r/gnoland/hof" + "gno.land/r/demo/hof" "gno.land/r/leon/config" ) From dd528a50f2ac108c13eae5bbfac223c17ebbf99a Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 18:40:57 +0200 Subject: [PATCH 47/71] newline --- examples/gno.land/r/demo/hof/rendering.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index fcb54d01089..8eb94692b76 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -70,6 +70,7 @@ func RenderExhibWidget(itemsToRender int) string { if itemsToRender < 1 { return "" } + if exhibition.items.Size() == 0 { return "" } From 57fbda658edbb33ef229d22b486419e6fe476e20 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 19:08:22 +0200 Subject: [PATCH 48/71] add tests --- examples/gno.land/r/demo/hof/hof_test.gno | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 examples/gno.land/r/demo/hof/hof_test.gno diff --git a/examples/gno.land/r/demo/hof/hof_test.gno b/examples/gno.land/r/demo/hof/hof_test.gno new file mode 100644 index 00000000000..18dbe11663f --- /dev/null +++ b/examples/gno.land/r/demo/hof/hof_test.gno @@ -0,0 +1,25 @@ +package hof + +import ( + "gno.land/p/demo/uassert" + "std" + "testing" + + "gno.land/p/demo/testutils" +) + +func TestRegister(t *testing.T) { + userRealm := std.NewUserRealm(testutils.TestAddress("leon")) + std.TestSetRealm(userRealm) + uassert.PanicsWithMessage(t, ErrNonCodeCall.Error(), Register) + + rlm1 := std.NewCodeRealm("gno.land/r/leon/home") + std.TestSetRealm(rlm1) + + Register() + Register() + //uassert.NotPanics(t, Register) + // + //Register() + //uassert.PanicsWithMessage(t, ErrAlreadyExists.Error(), Register) +} From e14f677a8f7010f63f6792df60090aada3002ab3 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 19:31:09 +0200 Subject: [PATCH 49/71] add sort avltree --- examples/gno.land/r/demo/hof/hof.gno | 60 ++++++++++++---------- examples/gno.land/r/demo/hof/rendering.gno | 6 +-- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index 2c727e6509e..c8cf9f3e844 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -21,7 +21,8 @@ type ( Exhibition struct { itemCounter seqid.ID description string - items *avl.Tree // Item id > Item + items *avl.Tree // pkgPath > Item + itemsNewest *avl.Tree // same data but sorted *pausable.Pausable } @@ -37,7 +38,8 @@ type ( func init() { exhibition = &Exhibition{ - items: avl.NewTree(), + items: avl.NewTree(), + itemsNewest: avl.NewTree(), } owner = ownable.NewWithAddress(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")) @@ -64,60 +66,66 @@ func Register() { } id := exhibition.itemCounter.Next() - exhibition.items.Set( - id.String(), - &Item{ - id: id, - pkgpath: pkgpath, - deployer: std.GetOrigCaller(), - blockNum: std.GetHeight(), - upvote: avl.NewTree(), - downvote: avl.NewTree(), - }) + i := &Item{ + id: id, + pkgpath: pkgpath, + deployer: std.GetOrigCaller(), + blockNum: std.GetHeight(), + upvote: avl.NewTree(), + downvote: avl.NewTree(), + } + + exhibition.items.Set(pkgpath, i) + exhibition.itemsNewest.Set(id.String(), i) } -func Upvote(itemID seqid.ID) error { - rawItem, ok := exhibition.items.Get(itemID.String()) +func Upvote(pkgpath string) { + rawItem, ok := exhibition.items.Get(pkgpath) if !ok { - return ErrNoSuchItem + panic(ErrNoSuchItem.Error()) } item := rawItem.(*Item) caller := std.PrevRealm().Addr().String() if item.upvote.Has(caller) { - return ErrDoubleUpvote + panic(ErrDoubleUpvote.Error()) } item.upvote.Set(caller, struct{}{}) - return nil } -func Downvote(itemID seqid.ID) error { - rawItem, ok := exhibition.items.Get(itemID.String()) +func Downvote(pkgpath string) { + rawItem, ok := exhibition.items.Get(pkgpath) if !ok { - return ErrNoSuchItem + panic(ErrNoSuchItem.Error()) } item := rawItem.(*Item) caller := std.PrevRealm().Addr().String() if item.downvote.Has(caller) { - return ErrDoubleDownvote + panic(ErrDoubleDownvote.Error()) } item.downvote.Set(caller, struct{}{}) - return nil } -func Delete(itemID seqid.ID) error { +func Delete(pkgpath string) { if err := owner.CallerIsOwner(); err != nil { panic(err) } - if _, removed := exhibition.items.Remove(itemID.String()); !removed { - return ErrNoSuchItem + i, ok := exhibition.items.Get(pkgpath) + if !ok { + panic(ErrNoSuchItem.Error()) + } + + if _, removed := exhibition.itemsNewest.Remove(i.(*Item).id.String()); !removed { + panic(ErrNoSuchItem.Error()) } - return nil + if _, removed := exhibition.items.Remove(pkgpath); !removed { + panic(ErrNoSuchItem.Error()) + } } diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index 8eb94692b76..ee8f1aa3bc4 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -8,7 +8,7 @@ import ( const ( likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&itemID=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&itemID=%s)\n\n" - delBar = "[Delete](/r/demo/hof?help&__func=Delete&itemID=%s)\n\n" + delBar = "[Delete](/r/demo/hof?help&__func=Delete&pkgpath=%s)\n\n" ) func Render(path string) string { @@ -28,7 +28,7 @@ func (e Exhibition) Render(dashboard bool) string { out += "
\n\n" - e.items.ReverseIterate("", "", func(key string, value interface{}) bool { + e.itemsNewest.ReverseIterate("", "", func(key string, value interface{}) bool { out += "
\n\n" id, _ := seqid.FromString(key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) @@ -59,7 +59,7 @@ func (i Item) Render(dashboard bool) string { if dashboard { out += ufmt.Sprintf( delBar, - i.id.String(), + i.pkgpath, ) } From d3de1e1e641e450dd20698b3bd3cc4c97a554f5c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 19:58:59 +0200 Subject: [PATCH 50/71] add tests --- examples/gno.land/r/demo/hof/hof.gno | 8 +- examples/gno.land/r/demo/hof/hof_test.gno | 100 +++++++++++++++++++-- examples/gno.land/r/demo/hof/rendering.gno | 8 +- 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index c8cf9f3e844..aa33ecad0ac 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -22,7 +22,7 @@ type ( itemCounter seqid.ID description string items *avl.Tree // pkgPath > Item - itemsNewest *avl.Tree // same data but sorted + itemsSorted *avl.Tree // same data but sorted, storing pointers *pausable.Pausable } @@ -39,7 +39,7 @@ type ( func init() { exhibition = &Exhibition{ items: avl.NewTree(), - itemsNewest: avl.NewTree(), + itemsSorted: avl.NewTree(), } owner = ownable.NewWithAddress(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")) @@ -76,7 +76,7 @@ func Register() { } exhibition.items.Set(pkgpath, i) - exhibition.itemsNewest.Set(id.String(), i) + exhibition.itemsSorted.Set(id.String(), i) } func Upvote(pkgpath string) { @@ -121,7 +121,7 @@ func Delete(pkgpath string) { panic(ErrNoSuchItem.Error()) } - if _, removed := exhibition.itemsNewest.Remove(i.(*Item).id.String()); !removed { + if _, removed := exhibition.itemsSorted.Remove(i.(*Item).id.String()); !removed { panic(ErrNoSuchItem.Error()) } diff --git a/examples/gno.land/r/demo/hof/hof_test.gno b/examples/gno.land/r/demo/hof/hof_test.gno index 18dbe11663f..67bc7b3f1e3 100644 --- a/examples/gno.land/r/demo/hof/hof_test.gno +++ b/examples/gno.land/r/demo/hof/hof_test.gno @@ -5,21 +5,103 @@ import ( "std" "testing" - "gno.land/p/demo/testutils" + "gno.land/p/demo/urequire" +) + +const rlm1Path = "gno.land/r/leon/home" + +var ( + admin = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") ) func TestRegister(t *testing.T) { - userRealm := std.NewUserRealm(testutils.TestAddress("leon")) + userRealm := std.NewUserRealm(admin) std.TestSetRealm(userRealm) uassert.PanicsWithMessage(t, ErrNonCodeCall.Error(), Register) - rlm1 := std.NewCodeRealm("gno.land/r/leon/home") + rlm1 := std.NewCodeRealm(rlm1Path) std.TestSetRealm(rlm1) + urequire.NotPanics(t, Register) + uassert.PanicsWithMessage(t, ErrAlreadyExists.Error(), Register) + + // Find registered items + i, ok := exhibition.items.Get(rlm1Path) + uassert.True(t, ok) + + _, ok = exhibition.itemsSorted.Get(i.(*Item).id.String()) + uassert.True(t, ok) +} + +func TestUpvote(t *testing.T) { + raw, _ := exhibition.items.Get(rlm1Path) + item := raw.(*Item) + + rawSorted, _ := exhibition.itemsSorted.Get(item.id.String()) + itemSorted := rawSorted.(*Item) + + // 0 upvotes by default + urequire.Equal(t, item.upvote.Size(), 0) + + userRealm := std.NewUserRealm(admin) + std.TestSetRealm(userRealm) + + urequire.NotPanics(t, func() { + Upvote(rlm1Path) + }) + + // Check both trees for 1 upvote + uassert.Equal(t, item.upvote.Size(), 1) + uassert.Equal(t, itemSorted.upvote.Size(), 1) + + // Check double upvote + uassert.PanicsWithMessage(t, ErrDoubleUpvote.Error(), func() { + Upvote(rlm1Path) + }) +} + +func TestDownvote(t *testing.T) { + raw, _ := exhibition.items.Get(rlm1Path) + item := raw.(*Item) + + rawSorted, _ := exhibition.itemsSorted.Get(item.id.String()) + itemSorted := rawSorted.(*Item) + + // 0 upvotes by default + urequire.Equal(t, item.downvote.Size(), 0) + + userRealm := std.NewUserRealm(admin) + std.TestSetRealm(userRealm) + + urequire.NotPanics(t, func() { + Downvote(rlm1Path) + }) + + // Check both trees for 1 upvote + uassert.Equal(t, item.downvote.Size(), 1) + uassert.Equal(t, itemSorted.downvote.Size(), 1) + + // Check double upvote + uassert.PanicsWithMessage(t, ErrDoubleDownvote.Error(), func() { + Downvote(rlm1Path) + }) +} + +func TestDelete(t *testing.T) { + userRealm := std.NewUserRealm(admin) + std.TestSetRealm(userRealm) + std.TestSetOrigCaller(admin) + + uassert.PanicsWithMessage(t, ErrNoSuchItem.Error(), func() { + Delete("nonexistentpkgpath") + }) + + i, ok := exhibition.items.Get(rlm1Path) + id := i.(*Item).id + + uassert.NotPanics(t, func() { + Delete(rlm1Path) + }) - Register() - Register() - //uassert.NotPanics(t, Register) - // - //Register() - //uassert.PanicsWithMessage(t, ErrAlreadyExists.Error(), Register) + uassert.False(t, exhibition.items.Has(rlm1Path)) + uassert.False(t, exhibition.itemsSorted.Has(id.String())) } diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index ee8f1aa3bc4..a7052f7f70b 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -7,7 +7,7 @@ import ( ) const ( - likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&itemID=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&itemID=%s)\n\n" + likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&pkgpath=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&pkgPath=%s)\n\n" delBar = "[Delete](/r/demo/hof?help&__func=Delete&pkgpath=%s)\n\n" ) @@ -28,7 +28,7 @@ func (e Exhibition) Render(dashboard bool) string { out += "
\n\n" - e.itemsNewest.ReverseIterate("", "", func(key string, value interface{}) bool { + e.itemsSorted.ReverseIterate("", "", func(key string, value interface{}) bool { out += "
\n\n" id, _ := seqid.FromString(key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) @@ -51,9 +51,9 @@ func (i Item) Render(dashboard bool) string { out += ufmt.Sprintf( likesBar, i.upvote.Size(), - i.id.String(), + i.pkgpath, i.downvote.Size(), - i.id.String(), + i.pkgpath, ) if dashboard { From 04061a85802d23ab28fcb9a5b7f739f137049df5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 20:04:31 +0200 Subject: [PATCH 51/71] add tests --- examples/gno.land/r/demo/hof/administration.gno | 4 ++-- examples/gno.land/r/demo/hof/errors.gno | 1 + examples/gno.land/r/demo/hof/hof.gno | 2 +- examples/gno.land/r/demo/hof/hof_test.gno | 11 +++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/demo/hof/administration.gno b/examples/gno.land/r/demo/hof/administration.gno index 59ce30d8c94..4b5b212eddf 100644 --- a/examples/gno.land/r/demo/hof/administration.gno +++ b/examples/gno.land/r/demo/hof/administration.gno @@ -6,11 +6,11 @@ import "std" // Should not be needed as soon as MsgCall supports calling methods on exported variables func Pause() error { - return exhibition.Pausable.Pause() + return exhibition.Pause() } func Unpause() error { - return exhibition.Pausable.Unpause() + return exhibition.Unpause() } func GetOwner() std.Address { diff --git a/examples/gno.land/r/demo/hof/errors.gno b/examples/gno.land/r/demo/hof/errors.gno index d903809fce4..29b84ee5dbf 100644 --- a/examples/gno.land/r/demo/hof/errors.gno +++ b/examples/gno.land/r/demo/hof/errors.gno @@ -10,4 +10,5 @@ var ( ErrDoubleUpvote = errors.New("hof: cannot upvote twice") ErrDoubleDownvote = errors.New("hof: cannot downvote twice") ErrNonCodeCall = errors.New("hof: non-code call") + ErrPaused = errors.New("hof: registering is paused") ) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index aa33ecad0ac..669762d1e37 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -50,7 +50,7 @@ func init() { // Should be called from within code func Register() { if exhibition.IsPaused() { - panic("hof: registering is paused") + panic(ErrPaused.Error()) } submission := std.PrevRealm() diff --git a/examples/gno.land/r/demo/hof/hof_test.gno b/examples/gno.land/r/demo/hof/hof_test.gno index 67bc7b3f1e3..d49f4e72601 100644 --- a/examples/gno.land/r/demo/hof/hof_test.gno +++ b/examples/gno.land/r/demo/hof/hof_test.gno @@ -1,18 +1,16 @@ package hof import ( - "gno.land/p/demo/uassert" "std" "testing" + "gno.land/p/demo/uassert" "gno.land/p/demo/urequire" ) const rlm1Path = "gno.land/r/leon/home" -var ( - admin = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") -) +var admin = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") func TestRegister(t *testing.T) { userRealm := std.NewUserRealm(admin) @@ -30,6 +28,11 @@ func TestRegister(t *testing.T) { _, ok = exhibition.itemsSorted.Get(i.(*Item).id.String()) uassert.True(t, ok) + + // Check paused + std.TestSetRealm(userRealm) + urequire.NotPanics(t, func() { exhibition.Pause() }) + uassert.PanicsWithMessage(t, ErrPaused.Error(), Register) } func TestUpvote(t *testing.T) { From c3ea2046b3d656b8ca8e9560d1345d6163bbf334 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 7 Oct 2024 20:14:12 +0200 Subject: [PATCH 52/71] add godoc --- examples/gno.land/r/demo/hof/hof.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index 669762d1e37..2962528e7bf 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -1,6 +1,6 @@ -// Package hof is the gno.land hall of fame realm. -// It contains a permanent exhibition of items in the hall of fame, -// a possibility to host temporary ones created by its admins. +// Package hof is the hall of fame realm. +// The Hall of Fame is an exhibition that holds items. Users can add their realms to the Hall of Fame by +// importing the Hall of Fame realm and calling hof.Register() from their init function. package hof import ( From f32070fe3b36ea1538c050060b4882630d810121 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 12:11:40 +0200 Subject: [PATCH 53/71] ownable --- examples/gno.land/p/demo/ownable/ownable.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index 1c1f7627266..48a1c15fffa 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -37,8 +37,8 @@ func (o *Ownable) TransferOwnership(newOwner std.Address) error { o.owner = newOwner std.Emit( OwnershipTransferEvent, - "from", string(prevOwner), - "to", string(newOwner), + "from", prevOwner.String(), + "to", newOwner.String(), ) return nil From 822ae4b78c0f6f77b8dd16bcf849f6236faabe1a Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 15:07:32 +0200 Subject: [PATCH 54/71] register to hof, add pagination --- .../gno.land/p/demo/avl/pager/z_filetest.gno | 1 - examples/gno.land/r/demo/foo20/foo20.gno | 2 ++ examples/gno.land/r/demo/foo20/gno.mod | 1 + examples/gno.land/r/demo/hof/gno.mod | 3 +++ examples/gno.land/r/demo/hof/rendering.gno | 21 ++++++++++++------- examples/gno.land/r/demo/userbook/gno.mod | 1 + .../gno.land/r/demo/userbook/userbook.gno | 2 ++ examples/gno.land/r/demo/users/users.gno | 3 ++- examples/gno.land/r/manfred/home/gno.mod | 5 ++++- examples/gno.land/r/manfred/home/home.gno | 6 +++++- examples/gno.land/r/morgan/guestbook/gno.mod | 1 + .../gno.land/r/morgan/guestbook/guestbook.gno | 2 ++ examples/gno.land/r/morgan/home/gno.mod | 2 ++ examples/gno.land/r/morgan/home/home.gno | 4 ++++ 14 files changed, 42 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 91c20115469..da4dd772438 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -14,7 +14,6 @@ func main() { for i := 0; i < 42; i++ { tree.Set(id.Next().String(), i) } - // Create a new pager. pager := pager.NewPager(tree, 7) diff --git a/examples/gno.land/r/demo/foo20/foo20.gno b/examples/gno.land/r/demo/foo20/foo20.gno index 9d4e5d40193..d1663e0d50b 100644 --- a/examples/gno.land/r/demo/foo20/foo20.gno +++ b/examples/gno.land/r/demo/foo20/foo20.gno @@ -3,6 +3,7 @@ package foo20 import ( + "gno.land/r/demo/hof" "std" "strings" @@ -24,6 +25,7 @@ func init() { banker = grc20.NewBanker("Foo", "FOO", 4) banker.Mint(admin.Owner(), 1000000*10000) // @administrator (1M) token = banker.Token() + hof.Register() } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/examples/gno.land/r/demo/foo20/gno.mod b/examples/gno.land/r/demo/foo20/gno.mod index 4035f9b1200..d33b672d291 100644 --- a/examples/gno.land/r/demo/foo20/gno.mod +++ b/examples/gno.land/r/demo/foo20/gno.mod @@ -7,5 +7,6 @@ require ( gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest + gno.land/r/demo/hof v0.0.0-latest gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod index ec5c5660311..bcaecaa1c76 100644 --- a/examples/gno.land/r/demo/hof/gno.mod +++ b/examples/gno.land/r/demo/hof/gno.mod @@ -2,9 +2,12 @@ module gno.land/r/demo/hof require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/avl/pager v0.0.0-latest gno.land/p/demo/fqname v0.0.0-latest gno.land/p/demo/ownable v0.0.0-latest gno.land/p/demo/pausable v0.0.0-latest gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/urequire v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index a7052f7f70b..e6596a0f0dc 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -1,8 +1,10 @@ package hof import ( + "gno.land/p/demo/avl/pager" "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" ) @@ -13,12 +15,12 @@ const ( func Render(path string) string { out := "# Hall of Fame\n\n" - out += exhibition.Render(path == "dashboard") + out += exhibition.Render(path, path == "dashboard") return out } -func (e Exhibition) Render(dashboard bool) string { +func (e Exhibition) Render(path string, dashboard bool) string { out := ufmt.Sprintf("%s\n\n", e.description) if e.items.Size() == 0 { @@ -28,18 +30,21 @@ func (e Exhibition) Render(dashboard bool) string { out += "
\n\n" - e.itemsSorted.ReverseIterate("", "", func(key string, value interface{}) bool { + p := pager.NewPager(e.itemsSorted, 3).MustGetPageByPath(path) + + for _, pgItem := range p.Items { + i := pgItem.Value.(*Item) out += "
\n\n" - id, _ := seqid.FromString(key) + id, _ := seqid.FromString(pgItem.Key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) - out += value.(*Item).Render(dashboard) + out += i.Render(dashboard) out += "
" - - return false - }) + } out += "
\n\n" + out += p.Selector() + return out } diff --git a/examples/gno.land/r/demo/userbook/gno.mod b/examples/gno.land/r/demo/userbook/gno.mod index 213586d12ee..12e9d53cb7a 100644 --- a/examples/gno.land/r/demo/userbook/gno.mod +++ b/examples/gno.land/r/demo/userbook/gno.mod @@ -5,4 +5,5 @@ require ( gno.land/p/demo/mux v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/userbook/userbook.gno b/examples/gno.land/r/demo/userbook/userbook.gno index c49bd90fa42..e6c9fe162f0 100644 --- a/examples/gno.land/r/demo/userbook/userbook.gno +++ b/examples/gno.land/r/demo/userbook/userbook.gno @@ -2,6 +2,7 @@ package userbook import ( + "gno.land/r/demo/hof" "std" "strconv" @@ -40,6 +41,7 @@ func init() { router.HandleFunc("", renderHelper) router.HandleFunc(subPath, renderHelper) + hof.Register() // Sign up the deployer SignUp() } diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 46ce3945bcd..1eaf3ac07e3 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,13 +328,14 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - page := pager.NewPager(&name2User, 50).MustGetPageByPath(path) + page := pager.NewPager(&name2User, 4).MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" } + doc += "\n" doc += page.Selector() return doc } diff --git a/examples/gno.land/r/manfred/home/gno.mod b/examples/gno.land/r/manfred/home/gno.mod index 6e7aac70cc7..9885cac19c2 100644 --- a/examples/gno.land/r/manfred/home/gno.mod +++ b/examples/gno.land/r/manfred/home/gno.mod @@ -1,3 +1,6 @@ module gno.land/r/manfred/home -require gno.land/r/manfred/config v0.0.0-latest +require ( + gno.land/r/demo/hof v0.0.0-latest + gno.land/r/manfred/config v0.0.0-latest +) diff --git a/examples/gno.land/r/manfred/home/home.gno b/examples/gno.land/r/manfred/home/home.gno index 720796a2201..4766f54e51f 100644 --- a/examples/gno.land/r/manfred/home/home.gno +++ b/examples/gno.land/r/manfred/home/home.gno @@ -1,6 +1,9 @@ package home -import "gno.land/r/manfred/config" +import ( + "gno.land/r/demo/hof" + "gno.land/r/manfred/config" +) var ( todos []string @@ -12,6 +15,7 @@ func init() { todos = append(todos, "fill this todo list...") status = "Online" // Initial status set to "Online" memeImgURL = "https://i.imgflip.com/7ze8dc.jpg" + hof.Register() } func Render(path string) string { diff --git a/examples/gno.land/r/morgan/guestbook/gno.mod b/examples/gno.land/r/morgan/guestbook/gno.mod index 2591643d33d..7a68430429e 100644 --- a/examples/gno.land/r/morgan/guestbook/gno.mod +++ b/examples/gno.land/r/morgan/guestbook/gno.mod @@ -4,4 +4,5 @@ require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/ownable v0.0.0-latest gno.land/p/demo/seqid v0.0.0-latest + gno.land/r/demo/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/morgan/guestbook/guestbook.gno b/examples/gno.land/r/morgan/guestbook/guestbook.gno index b3a56d88397..92a588aa822 100644 --- a/examples/gno.land/r/morgan/guestbook/guestbook.gno +++ b/examples/gno.land/r/morgan/guestbook/guestbook.gno @@ -3,6 +3,7 @@ package guestbook import ( + "gno.land/r/demo/hof" "std" "strconv" "strings" @@ -33,6 +34,7 @@ var ( func init() { Sign("You reached the end of the guestbook!") + hof.Register() } const ( diff --git a/examples/gno.land/r/morgan/home/gno.mod b/examples/gno.land/r/morgan/home/gno.mod index 573a7e139e7..35e2fbb2119 100644 --- a/examples/gno.land/r/morgan/home/gno.mod +++ b/examples/gno.land/r/morgan/home/gno.mod @@ -1 +1,3 @@ module gno.land/r/morgan/home + +require gno.land/r/demo/hof v0.0.0-latest diff --git a/examples/gno.land/r/morgan/home/home.gno b/examples/gno.land/r/morgan/home/home.gno index 33d7e0b2df7..571f14ed5ec 100644 --- a/examples/gno.land/r/morgan/home/home.gno +++ b/examples/gno.land/r/morgan/home/home.gno @@ -1,10 +1,14 @@ package home +import "gno.land/r/demo/hof" + const staticHome = `# morgan's (gn)home - [📝 sign my guestbook](/r/morgan/guestbook) ` +func init() { hof.Register() } + func Render(path string) string { return staticHome } From 5b7afc85615a36079b2c88b7344c883e61cca0e7 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 15:48:41 +0200 Subject: [PATCH 55/71] renaming --- examples/gno.land/r/demo/hof/rendering.gno | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index e6596a0f0dc..4a268a6d6dd 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -30,20 +30,19 @@ func (e Exhibition) Render(path string, dashboard bool) string { out += "
\n\n" - p := pager.NewPager(e.itemsSorted, 3).MustGetPageByPath(path) + page := pager.NewPager(e.itemsSorted, 3).MustGetPageByPath(path) - for _, pgItem := range p.Items { - i := pgItem.Value.(*Item) + for _, item := range page.Items { out += "
\n\n" - id, _ := seqid.FromString(pgItem.Key) + id, _ := seqid.FromString(item.Key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) - out += i.Render(dashboard) + out += item.Value.(*Item).Render(dashboard) out += "
" } out += "
\n\n" - out += p.Selector() + out += page.Selector() return out } From b40e1284510062e7003f4ccc764f8566b9ed5b68 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 16:16:53 +0200 Subject: [PATCH 56/71] add codeowner --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3870ff30539..899a6b392fc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -39,6 +39,7 @@ /examples/gno.land/r/demo/art/ @moul /examples/gno.land/r/demo/boardsv2/ @ilgooz @jeronimoalbi @moul /examples/gno.land/r/demo/memeland/ @leohhhn +/examples/gno.land/r/demo/hof/ @leohhhn /examples/gno.land/r/demo/tamagotchi/ @moul /examples/gno.land/r/demo/userbook/ @leohhhn /examples/gno.land/r/gnoland/ @moul From 8a55c2a06f8746424de2b5f96680f7a69481738e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 16:22:12 +0200 Subject: [PATCH 57/71] add codeowner --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 899a6b392fc..9ac53c37cca 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -46,6 +46,7 @@ /examples/gno.land/r/sys/ @moul /examples/gno.land/r/jaekwon/ @jaekwon /examples/gno.land/r/manfred/ @moul +/examples/gno.land/r/leon/ @leohhhn # Gno.land. /gno.land/ @moul @zivkovicmilos From edc6915502f864afa548a9266d2df09ca795001a Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 19:37:36 +0200 Subject: [PATCH 58/71] rm hof --- examples/gno.land/r/demo/foo20/foo20.gno | 2 -- examples/gno.land/r/demo/foo20/gno.mod | 1 - examples/gno.land/r/demo/hof/hof.gno | 2 -- examples/gno.land/r/demo/hof/rendering.gno | 6 ++++-- examples/gno.land/r/demo/userbook/gno.mod | 1 - examples/gno.land/r/demo/userbook/userbook.gno | 2 -- examples/gno.land/r/morgan/guestbook/gno.mod | 1 - examples/gno.land/r/morgan/guestbook/guestbook.gno | 2 -- 8 files changed, 4 insertions(+), 13 deletions(-) diff --git a/examples/gno.land/r/demo/foo20/foo20.gno b/examples/gno.land/r/demo/foo20/foo20.gno index d1663e0d50b..9d4e5d40193 100644 --- a/examples/gno.land/r/demo/foo20/foo20.gno +++ b/examples/gno.land/r/demo/foo20/foo20.gno @@ -3,7 +3,6 @@ package foo20 import ( - "gno.land/r/demo/hof" "std" "strings" @@ -25,7 +24,6 @@ func init() { banker = grc20.NewBanker("Foo", "FOO", 4) banker.Mint(admin.Owner(), 1000000*10000) // @administrator (1M) token = banker.Token() - hof.Register() } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/examples/gno.land/r/demo/foo20/gno.mod b/examples/gno.land/r/demo/foo20/gno.mod index d33b672d291..4035f9b1200 100644 --- a/examples/gno.land/r/demo/foo20/gno.mod +++ b/examples/gno.land/r/demo/foo20/gno.mod @@ -7,6 +7,5 @@ require ( gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest - gno.land/r/demo/hof v0.0.0-latest gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index 2962528e7bf..cca85e41cd1 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -29,7 +29,6 @@ type ( Item struct { id seqid.ID pkgpath string - deployer std.Address blockNum int64 upvote *avl.Tree // std.Addr > struct{}{} downvote *avl.Tree // std.Addr > struct{}{} @@ -69,7 +68,6 @@ func Register() { i := &Item{ id: id, pkgpath: pkgpath, - deployer: std.GetOrigCaller(), blockNum: std.GetHeight(), upvote: avl.NewTree(), downvote: avl.NewTree(), diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index 4a268a6d6dd..992d855232b 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -4,6 +4,7 @@ import ( "gno.land/p/demo/avl/pager" "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" + "strings" "gno.land/p/demo/ufmt" ) @@ -11,6 +12,7 @@ import ( const ( likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&pkgpath=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&pkgPath=%s)\n\n" delBar = "[Delete](/r/demo/hof?help&__func=Delete&pkgpath=%s)\n\n" + pageSize = 20 ) func Render(path string) string { @@ -30,7 +32,7 @@ func (e Exhibition) Render(path string, dashboard bool) string { out += "
\n\n" - page := pager.NewPager(e.itemsSorted, 3).MustGetPageByPath(path) + page := pager.NewPager(e.itemsSorted, pageSize).MustGetPageByPath(path) for _, item := range page.Items { out += "
\n\n" @@ -49,7 +51,7 @@ func (e Exhibition) Render(path string, dashboard bool) string { func (i Item) Render(dashboard bool) string { out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) - out += ufmt.Sprintf("by %s\n\n", i.deployer.String()) + out += ufmt.Sprintf("by %s\n\n", strings.Split(i.pkgpath, "/")[2]) out += ufmt.Sprintf("[View realm](%s)\n\n", fqname.ShortPath(i.pkgpath)) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) out += ufmt.Sprintf( diff --git a/examples/gno.land/r/demo/userbook/gno.mod b/examples/gno.land/r/demo/userbook/gno.mod index 12e9d53cb7a..213586d12ee 100644 --- a/examples/gno.land/r/demo/userbook/gno.mod +++ b/examples/gno.land/r/demo/userbook/gno.mod @@ -5,5 +5,4 @@ require ( gno.land/p/demo/mux v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest - gno.land/r/demo/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/userbook/userbook.gno b/examples/gno.land/r/demo/userbook/userbook.gno index e6c9fe162f0..c49bd90fa42 100644 --- a/examples/gno.land/r/demo/userbook/userbook.gno +++ b/examples/gno.land/r/demo/userbook/userbook.gno @@ -2,7 +2,6 @@ package userbook import ( - "gno.land/r/demo/hof" "std" "strconv" @@ -41,7 +40,6 @@ func init() { router.HandleFunc("", renderHelper) router.HandleFunc(subPath, renderHelper) - hof.Register() // Sign up the deployer SignUp() } diff --git a/examples/gno.land/r/morgan/guestbook/gno.mod b/examples/gno.land/r/morgan/guestbook/gno.mod index 7a68430429e..2591643d33d 100644 --- a/examples/gno.land/r/morgan/guestbook/gno.mod +++ b/examples/gno.land/r/morgan/guestbook/gno.mod @@ -4,5 +4,4 @@ require ( gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/ownable v0.0.0-latest gno.land/p/demo/seqid v0.0.0-latest - gno.land/r/demo/hof v0.0.0-latest ) diff --git a/examples/gno.land/r/morgan/guestbook/guestbook.gno b/examples/gno.land/r/morgan/guestbook/guestbook.gno index 92a588aa822..b3a56d88397 100644 --- a/examples/gno.land/r/morgan/guestbook/guestbook.gno +++ b/examples/gno.land/r/morgan/guestbook/guestbook.gno @@ -3,7 +3,6 @@ package guestbook import ( - "gno.land/r/demo/hof" "std" "strconv" "strings" @@ -34,7 +33,6 @@ var ( func init() { Sign("You reached the end of the guestbook!") - hof.Register() } const ( From f31c2e9f87a4aa2267277917823e955458abf70f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 20:02:35 +0200 Subject: [PATCH 59/71] fix tests, fmt --- examples/gno.land/r/demo/hof/rendering.gno | 7 +++++-- examples/gno.land/r/demo/users/z_5_filetest.gno | 10 ++++------ examples/gno.land/r/gnoland/home/home_filetest.gno | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index 992d855232b..d3a73ebbe4a 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -1,10 +1,11 @@ package hof import ( + "strings" + "gno.land/p/demo/avl/pager" "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" - "strings" "gno.land/p/demo/ufmt" ) @@ -34,7 +35,9 @@ func (e Exhibition) Render(path string, dashboard bool) string { page := pager.NewPager(e.itemsSorted, pageSize).MustGetPageByPath(path) - for _, item := range page.Items { + for i := len(page.Items) - 1; i >= 0; i-- { + item := page.Items[i] + out += "
\n\n" id, _ := seqid.FromString(item.Key) out += ufmt.Sprintf("### Submission #%d\n\n", int(id)) diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index ee213c3c73f..a8b5c2d1721 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -42,17 +42,15 @@ func main() { // * [demo](/r/demo/users:demo) // * [gno](/r/demo/users:gno) // * [gnoland](/r/demo/users:gnoland) +// +// **1** | [2](?page=2) | [3](?page=3) +// ======================================== // * [gnolang](/r/demo/users:gnolang) // * [gnouser](/r/demo/users:gnouser) // * [gov](/r/demo/users:gov) // * [nt](/r/demo/users:nt) -// * [satoshi](/r/demo/users:satoshi) -// * [sys](/r/demo/users:sys) -// * [test1](/r/demo/users:test1) -// * [x](/r/demo/users:x) -// -// ======================================== // +// [1](?page=1) | **2** | [3](?page=3) // ======================================== // ## user gnouser // diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 2260dc3a409..8c10f8db1de 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -78,9 +78,9 @@ func main() { //
//
// -// ### Latest Contributions +// ### [Hall of Fame](/r/demo/hof) +// // -// [View latest contributions](https://github.com/gnolang/gno/pulls) //
//
// From 76023797506163e92cbf98da5445e7db17f1594b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 8 Oct 2024 20:05:59 +0200 Subject: [PATCH 60/71] format import --- examples/gno.land/r/demo/hof/rendering.gno | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index d3a73ebbe4a..ab290545d75 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -6,7 +6,6 @@ import ( "gno.land/p/demo/avl/pager" "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" ) From 8bcf672fe64a26fe6a6ad307bf04a732282b3504 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 15 Oct 2024 20:07:48 +0200 Subject: [PATCH 61/71] rm panics, just return --- examples/gno.land/r/demo/hof/hof.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index cca85e41cd1..3070bbe10da 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -49,7 +49,7 @@ func init() { // Should be called from within code func Register() { if exhibition.IsPaused() { - panic(ErrPaused.Error()) + return } submission := std.PrevRealm() @@ -57,11 +57,11 @@ func Register() { // Must not yet exist and must be called from code if submission.IsUser() { - panic(ErrNonCodeCall.Error()) + return } if exhibition.items.Has(pkgpath) { - panic(ErrAlreadyExists.Error()) + return } id := exhibition.itemCounter.Next() From 3f35e680bd31e774e35518f2d0be76853f66cef4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 15 Oct 2024 20:29:38 +0200 Subject: [PATCH 62/71] fix tests --- examples/gno.land/r/demo/hof/errors.gno | 3 - examples/gno.land/r/demo/hof/hof_test.gno | 85 +++++++++++++++-------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/examples/gno.land/r/demo/hof/errors.gno b/examples/gno.land/r/demo/hof/errors.gno index 29b84ee5dbf..7277f65fa76 100644 --- a/examples/gno.land/r/demo/hof/errors.gno +++ b/examples/gno.land/r/demo/hof/errors.gno @@ -6,9 +6,6 @@ import ( var ( ErrNoSuchItem = errors.New("hof: no such item exists") - ErrAlreadyExists = errors.New("hof: alerady exists") ErrDoubleUpvote = errors.New("hof: cannot upvote twice") ErrDoubleDownvote = errors.New("hof: cannot downvote twice") - ErrNonCodeCall = errors.New("hof: non-code call") - ErrPaused = errors.New("hof: registering is paused") ) diff --git a/examples/gno.land/r/demo/hof/hof_test.gno b/examples/gno.land/r/demo/hof/hof_test.gno index d49f4e72601..125e6e72a99 100644 --- a/examples/gno.land/r/demo/hof/hof_test.gno +++ b/examples/gno.land/r/demo/hof/hof_test.gno @@ -4,39 +4,52 @@ import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" "gno.land/p/demo/urequire" ) -const rlm1Path = "gno.land/r/leon/home" +const rlmPath = "gno.land/r/gnoland/home" -var admin = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") +var ( + admin = owner.Owner() + adminRealm = std.NewUserRealm(admin) + alice = testutils.TestAddress("alice") +) func TestRegister(t *testing.T) { - userRealm := std.NewUserRealm(admin) - std.TestSetRealm(userRealm) - uassert.PanicsWithMessage(t, ErrNonCodeCall.Error(), Register) - rlm1 := std.NewCodeRealm(rlm1Path) - std.TestSetRealm(rlm1) - urequire.NotPanics(t, Register) - uassert.PanicsWithMessage(t, ErrAlreadyExists.Error(), Register) + // Test user realm register + aliceRealm := std.NewUserRealm(alice) + std.TestSetRealm(aliceRealm) - // Find registered items - i, ok := exhibition.items.Get(rlm1Path) - uassert.True(t, ok) + Register() + uassert.False(t, itemExists(t, rlmPath)) - _, ok = exhibition.itemsSorted.Get(i.(*Item).id.String()) - uassert.True(t, ok) + // Test register while paused + std.TestSetRealm(adminRealm) + Pause() - // Check paused - std.TestSetRealm(userRealm) - urequire.NotPanics(t, func() { exhibition.Pause() }) - uassert.PanicsWithMessage(t, ErrPaused.Error(), Register) + // Set legitimate caller + std.TestSetRealm(std.NewCodeRealm(rlmPath)) + + Register() + uassert.False(t, itemExists(t, rlmPath)) + + // Unpause + std.TestSetRealm(adminRealm) + Unpause() + + // Set legitimate caller + std.TestSetRealm(std.NewCodeRealm(rlmPath)) + Register() + + // Find registered items + uassert.True(t, itemExists(t, rlmPath)) } func TestUpvote(t *testing.T) { - raw, _ := exhibition.items.Get(rlm1Path) + raw, _ := exhibition.items.Get(rlmPath) item := raw.(*Item) rawSorted, _ := exhibition.itemsSorted.Get(item.id.String()) @@ -45,11 +58,10 @@ func TestUpvote(t *testing.T) { // 0 upvotes by default urequire.Equal(t, item.upvote.Size(), 0) - userRealm := std.NewUserRealm(admin) - std.TestSetRealm(userRealm) + std.TestSetRealm(adminRealm) urequire.NotPanics(t, func() { - Upvote(rlm1Path) + Upvote(rlmPath) }) // Check both trees for 1 upvote @@ -58,12 +70,12 @@ func TestUpvote(t *testing.T) { // Check double upvote uassert.PanicsWithMessage(t, ErrDoubleUpvote.Error(), func() { - Upvote(rlm1Path) + Upvote(rlmPath) }) } func TestDownvote(t *testing.T) { - raw, _ := exhibition.items.Get(rlm1Path) + raw, _ := exhibition.items.Get(rlmPath) item := raw.(*Item) rawSorted, _ := exhibition.itemsSorted.Get(item.id.String()) @@ -72,11 +84,11 @@ func TestDownvote(t *testing.T) { // 0 upvotes by default urequire.Equal(t, item.downvote.Size(), 0) - userRealm := std.NewUserRealm(admin) + userRealm := std.NewUserRealm(alice) std.TestSetRealm(userRealm) urequire.NotPanics(t, func() { - Downvote(rlm1Path) + Downvote(rlmPath) }) // Check both trees for 1 upvote @@ -85,7 +97,7 @@ func TestDownvote(t *testing.T) { // Check double upvote uassert.PanicsWithMessage(t, ErrDoubleDownvote.Error(), func() { - Downvote(rlm1Path) + Downvote(rlmPath) }) } @@ -98,13 +110,26 @@ func TestDelete(t *testing.T) { Delete("nonexistentpkgpath") }) - i, ok := exhibition.items.Get(rlm1Path) + i, _ := exhibition.items.Get(rlmPath) id := i.(*Item).id uassert.NotPanics(t, func() { - Delete(rlm1Path) + Delete(rlmPath) }) - uassert.False(t, exhibition.items.Has(rlm1Path)) + uassert.False(t, exhibition.items.Has(rlmPath)) uassert.False(t, exhibition.itemsSorted.Has(id.String())) } + +func itemExists(t *testing.T, rlmPath string) bool { + t.Helper() + + i, ok1 := exhibition.items.Get(rlmPath) + ok2 := false + + if ok1 { + _, ok2 = exhibition.itemsSorted.Get(i.(*Item).id.String()) + } + + return ok1 && ok2 +} From 2f88736e342caa4cdc9d2b1a32c934ea79fce361 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 15 Oct 2024 22:56:11 +0200 Subject: [PATCH 63/71] mod tidy --- examples/gno.land/r/demo/hof/gno.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/demo/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod index bcaecaa1c76..7c71e7c377e 100644 --- a/examples/gno.land/r/demo/hof/gno.mod +++ b/examples/gno.land/r/demo/hof/gno.mod @@ -7,6 +7,7 @@ require ( gno.land/p/demo/ownable v0.0.0-latest gno.land/p/demo/pausable v0.0.0-latest gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/urequire v0.0.0-latest From 1cc0a9edfb922a243d81c35d00b1f12a231c2747 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 16 Oct 2024 14:09:33 +0200 Subject: [PATCH 64/71] update codeowners --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0e7a033a368..55c64a2adae 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -47,6 +47,7 @@ /examples/gno.land/r/jaekwon/ @jaekwon /examples/gno.land/r/manfred/ @moul /examples/gno.land/r/leon/ @leohhhn +/examples/gno.land/r/morgan/ @thehowl # Gno.land. /gno.land/ @moul @zivkovicmilos From cd2814e2d9c50f4ab9369b6f1ab292b4a0031a46 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 22 Oct 2024 12:25:14 -0500 Subject: [PATCH 65/71] using helplink --- examples/gno.land/r/demo/hof/gno.mod | 1 + examples/gno.land/r/demo/hof/rendering.gno | 19 +++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/r/demo/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod index 7c71e7c377e..ec7353e158f 100644 --- a/examples/gno.land/r/demo/hof/gno.mod +++ b/examples/gno.land/r/demo/hof/gno.mod @@ -11,4 +11,5 @@ require ( gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/urequire v0.0.0-latest + gno.land/p/moul/helplink v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index ab290545d75..ff82c58b620 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -7,16 +7,16 @@ import ( "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" + hl "gno.land/p/moul/helplink" ) const ( - likesBar = "#### [%d 👍](/r/demo/hof?help&__func=Upvote&pkgpath=%s) - [%d 👎](/r/demo/hof?help&__func=Downvote&pkgPath=%s)\n\n" - delBar = "[Delete](/r/demo/hof?help&__func=Delete&pkgpath=%s)\n\n" pageSize = 20 ) func Render(path string) string { out := "# Hall of Fame\n\n" + out += exhibition.Render(path, path == "dashboard") return out @@ -56,19 +56,14 @@ func (i Item) Render(dashboard bool) string { out += ufmt.Sprintf("by %s\n\n", strings.Split(i.pkgpath, "/")[2]) out += ufmt.Sprintf("[View realm](%s)\n\n", fqname.ShortPath(i.pkgpath)) out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) - out += ufmt.Sprintf( - likesBar, - i.upvote.Size(), - i.pkgpath, - i.downvote.Size(), - i.pkgpath, + + out += ufmt.Sprintf("#### [%d👍](%s) - [%d👎](%s)\n\n", + i.upvote.Size(), hl.FuncURL("Upvote", "pkgpath", i.pkgpath), + i.downvote.Size(), hl.FuncURL("Downvote", "pkgpath", i.pkgpath), ) if dashboard { - out += ufmt.Sprintf( - delBar, - i.pkgpath, - ) + out += hl.Func("Delete", "Delete", "pkgpath", i.pkgpath) } return out From 40ba8547aa333933c3ff291a7d9b21092632cd0d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 25 Oct 2024 14:41:41 -0500 Subject: [PATCH 66/71] rm codeowners --- .github/CODEOWNERS | 100 --------------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 28d3a6fbd65..00000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,100 +0,0 @@ -# CODEOWNERS: https://help.github.com/articles/about-codeowners/ - -# Primary repo maintainers. -* @gnolang/tech-staff - -# Tendermint2. -/tm2/ @jaekwon @moul @piux2 @zivkovicmilos -/tm2/pkg/crypto/ @jaekwon @moul @gnolang/security -/tm2/pkg/crypto/keys/client/ @jaekwon @gnolang/security -/tm2/pkg/db/ @ajnavarro -# TODO: add per package exceptions -# ... - -# Docs & Content. -/docs/ @moul -/docs/**.md @gnolang/devrels -/docs/**.gif @gnolang/devrels -/docs/Makefile @gnolang/devrels -/README.md @moul @gnolang/devrels -/**/README.md @gnolang/devrels -/.gitpod.yml @gnolang/devrels - -# Gno examples and default contracts. -/examples/ @gnolang/tech-staff @gnolang/devrels -/examples/gno.land/p/demo/ @gnolang/tech-staff @gnolang/devrels -/examples/gno.land/p/demo/avl/ @jaekwon -/examples/gno.land/p/demo/bf/ @moul -/examples/gno.land/p/demo/blog/ @gnolang/devrels -/examples/gno.land/p/demo/boardsv2/ @ilgooz @jeronimoalbi @moul -/examples/gno.land/p/demo/cford32/ @thehowl -/examples/gno.land/p/demo/memeland/ @leohhhn -/examples/gno.land/p/demo/seqid/ @thehowl -/examples/gno.land/p/demo/ownable/ @leohhhn -/examples/gno.land/p/demo/pausable/ @leohhhn -/examples/gno.land/p/demo/svg/ @moul -/examples/gno.land/p/demo/tamagotchi/ @moul -/examples/gno.land/p/demo/ui/ @moul -/examples/gno.land/r/demo/ @gnolang/tech-staff @gnolang/devrels -/examples/gno.land/r/demo/art/ @moul -/examples/gno.land/r/demo/boardsv2/ @ilgooz @jeronimoalbi @moul -/examples/gno.land/r/demo/memeland/ @leohhhn -/examples/gno.land/r/demo/hof/ @leohhhn -/examples/gno.land/r/demo/tamagotchi/ @moul -/examples/gno.land/r/demo/userbook/ @leohhhn -/examples/gno.land/r/gnoland/ @moul -/examples/gno.land/r/sys/ @moul -/examples/gno.land/r/jaekwon/ @jaekwon -/examples/gno.land/r/manfred/ @moul -/examples/gno.land/r/leon/ @leohhhn -/examples/gno.land/r/morgan/ @thehowl - -# Gno.land. -/gno.land/ @moul @zivkovicmilos -/gno.land/cmd/genesis/ @zivkovicmilos -/gno.land/cmd/gnokey/ @jaekwon @moul @gfanton -/gno.land/cmd/gnoland/ @zivkovicmilos @gnolang/devops -/gno.land/cmd/gnoweb/ @gfanton @thehowl @alexiscolin -/gno.land/pkg/gnoclient/ @zivkovicmilos @leohhhn @gfanton -/gno.land/pkg/gnoland/ @zivkovicmilos @gfanton -/gno.land/pkg/keyscli/ @jaekwon @moul @gfanton -/gno.land/pkg/log/ @zivkovicmilos @gfanton -/gno.land/pkg/sdk/vm/ @moul @gfanton @thehowl -/gno.land/pkg/integration/ @gfanton -/gno.land/genesis/ @moul -#... - -# GnoVM/Gnolang. -/gnovm/ @jaekwon @moul @piux2 @thehowl -/gnovm/stdlibs/ @thehowl -/gnovm/tests/ @jaekwon @thehowl @mvertes -/gnovm/cmd/gno/ @moul @thehowl -/gnovm/pkg/gnolang/ @jaekwon @moul @piux2 -/gnovm/pkg/doc/ @thehowl -/gnovm/pkg/repl/ @mvertes @ajnavarro -/gnovm/pkg/gnomod/ @thehowl -/gnovm/pkg/gnoenv/ @gfanton -/gnovm/pkg/transpiler/ @thehowl -/gnovm/pkg/integration/ @gfanton - -# Contribs -/contribs/ @gnolang/tech-staff -/contribs/gnodev/ @gfanton -/contribs/gnokeykc/ @moul -/contribs/gnomd/ @moul - -# Misc -/misc/ @gnolang/tech-staff -/misc/loop/ @moul @gnolang/devops -/misc/deployments/ @moul @gnolang/devops -/misc/genstd/ @thehowl - -# Special files. -/PLAN.md @jaekwon @moul -/PHILOSOPHY.md @jaekwon -/CONTRIBUTING.md @jaekwon @moul @gnolang/tech-staff -/LICENSE.md @jaekwon -/.github/ @moul @gnolang/tech-staff -/.github/workflows @ajnavarro @moul -/.github/CODEOWNERS @jaekwon @moul -/go.mod @gnolang/tech-staff # no unnecessary dependencies From fe9092dd979628c95aac7a6dae7da7376fd9e7ad Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 25 Oct 2024 14:43:47 -0500 Subject: [PATCH 67/71] txlink --- examples/gno.land/r/demo/hof/rendering.gno | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index ff82c58b620..a6dd9962764 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -7,7 +7,7 @@ import ( "gno.land/p/demo/fqname" "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" - hl "gno.land/p/moul/helplink" + "gno.land/p/moul/txlink" ) const ( @@ -58,12 +58,12 @@ func (i Item) Render(dashboard bool) string { out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) out += ufmt.Sprintf("#### [%d👍](%s) - [%d👎](%s)\n\n", - i.upvote.Size(), hl.FuncURL("Upvote", "pkgpath", i.pkgpath), - i.downvote.Size(), hl.FuncURL("Downvote", "pkgpath", i.pkgpath), + i.upvote.Size(), txlink.URL("Upvote", "pkgpath", i.pkgpath), + i.downvote.Size(), txlink.URL("Downvote", "pkgpath", i.pkgpath), ) if dashboard { - out += hl.Func("Delete", "Delete", "pkgpath", i.pkgpath) + out += ufmt.Sprintf("[Delete](%s)", txlink.URL("Delete", "pkgpath", i.pkgpath) } return out From 72e50894e20271bef18aa688cf8df5d01f2b8224 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 25 Oct 2024 14:58:50 -0500 Subject: [PATCH 68/71] dashboard render --- examples/gno.land/r/demo/hof/hof.gno | 3 ++- examples/gno.land/r/demo/hof/rendering.gno | 30 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/demo/hof/hof.gno b/examples/gno.land/r/demo/hof/hof.gno index 3070bbe10da..b1eaf9138b2 100644 --- a/examples/gno.land/r/demo/hof/hof.gno +++ b/examples/gno.land/r/demo/hof/hof.gno @@ -55,11 +55,12 @@ func Register() { submission := std.PrevRealm() pkgpath := submission.PkgPath() - // Must not yet exist and must be called from code + // Must be called from code if submission.IsUser() { return } + // Must not yet exist if exhibition.items.Has(pkgpath) { return } diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index a6dd9962764..2b8f9e4f647 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -17,7 +17,13 @@ const ( func Render(path string) string { out := "# Hall of Fame\n\n" - out += exhibition.Render(path, path == "dashboard") + dashboardEnabled := path == "dashboard" + + if dashboardEnabled { + out += renderDashboard() + } + + out += exhibition.Render(path, dashboardEnabled) return out } @@ -55,7 +61,7 @@ func (i Item) Render(dashboard bool) string { out := ufmt.Sprintf("\n```\n%s\n```\n\n", i.pkgpath) out += ufmt.Sprintf("by %s\n\n", strings.Split(i.pkgpath, "/")[2]) out += ufmt.Sprintf("[View realm](%s)\n\n", fqname.ShortPath(i.pkgpath)) - out += ufmt.Sprintf("Published at Block #%d\n\n", i.blockNum) + out += ufmt.Sprintf("Submitted at Block #%d\n\n", i.blockNum) out += ufmt.Sprintf("#### [%d👍](%s) - [%d👎](%s)\n\n", i.upvote.Size(), txlink.URL("Upvote", "pkgpath", i.pkgpath), @@ -63,12 +69,30 @@ func (i Item) Render(dashboard bool) string { ) if dashboard { - out += ufmt.Sprintf("[Delete](%s)", txlink.URL("Delete", "pkgpath", i.pkgpath) + out += ufmt.Sprintf("[Delete](%s)", txlink.URL("Delete", "pkgpath", i.pkgpath)) } return out } +func renderDashboard() string { + out := "---\n\n" + out += "## Dashboard\n\n" + out += ufmt.Sprintf("Total submissions: %d\n\n", exhibition.items.Size()) + + out += ufmt.Sprintf("Exhibition admin: %s\n\n", owner.Owner().String()) + + if !exhibition.IsPaused() { + out += ufmt.Sprintf("[Pause exhibition](%s)\n\n", txlink.URL("Pause")) + } else { + out += ufmt.Sprintf("[Unpause exhibition](%s)\n\n", txlink.URL("Unpause")) + } + + out += "---\n\n" + + return out +} + func RenderExhibWidget(itemsToRender int) string { if itemsToRender < 1 { return "" From 3e5a640e4080187f60903909cc3f9da921801df0 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 25 Oct 2024 15:05:29 -0500 Subject: [PATCH 69/71] tidy --- examples/gno.land/r/demo/hof/gno.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/hof/gno.mod b/examples/gno.land/r/demo/hof/gno.mod index ec7353e158f..ac5c91295a6 100644 --- a/examples/gno.land/r/demo/hof/gno.mod +++ b/examples/gno.land/r/demo/hof/gno.mod @@ -11,5 +11,5 @@ require ( gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/urequire v0.0.0-latest - gno.land/p/moul/helplink v0.0.0-latest + gno.land/p/moul/txlink v0.0.0-latest ) From 882baf085d926a2686b04485e8602bdda9dbc660 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 7 Nov 2024 20:20:31 +0100 Subject: [PATCH 70/71] fix tests --- examples/gno.land/r/demo/users/z_5_filetest.gno | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index 2ee17bb10c9..2b3e1b17b5c 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -42,13 +42,14 @@ func main() { // * [demo](/r/demo/users:demo) // * [gno](/r/demo/users:gno) // * [gnoland](/r/demo/users:gnoland) -// -// **1** | [2](?page=2) | [3](?page=3) -// ======================================== // * [gnolang](/r/demo/users:gnolang) // * [gnouser](/r/demo/users:gnouser) // * [gov](/r/demo/users:gov) // * [nt](/r/demo/users:nt) +// * [satoshi](/r/demo/users:satoshi) +// * [sys](/r/demo/users:sys) +// * [test1](/r/demo/users:test1) +// * [x](/r/demo/users:x) // // // ======================================== From a43ccc1c623f5ee5e807301830c51d3f74718f6f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 7 Nov 2024 20:21:29 +0100 Subject: [PATCH 71/71] update page size --- examples/gno.land/r/demo/hof/rendering.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/hof/rendering.gno b/examples/gno.land/r/demo/hof/rendering.gno index 2b8f9e4f647..7f9a32568ce 100644 --- a/examples/gno.land/r/demo/hof/rendering.gno +++ b/examples/gno.land/r/demo/hof/rendering.gno @@ -11,7 +11,7 @@ import ( ) const ( - pageSize = 20 + pageSize = 5 ) func Render(path string) string {