diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 60bb44d97b6..cccdc0df645 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -15,6 +15,7 @@ type Pager struct { PageQueryParam string SizeQueryParam string DefaultPageSize int + Reversed bool } // Page represents a single page of results. @@ -36,12 +37,13 @@ type Item struct { } // NewPager creates a new Pager with default values. -func NewPager(tree *avl.Tree, defaultPageSize int) *Pager { +func NewPager(tree *avl.Tree, defaultPageSize int, reversed bool) *Pager { return &Pager{ Tree: tree, PageQueryParam: "page", SizeQueryParam: "size", DefaultPageSize: defaultPageSize, + Reversed: reversed, } } @@ -86,10 +88,18 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { } items := []Item{} - p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { - items = append(items, Item{Key: key, Value: value}) - return false - }) + + if p.Reversed { + p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + items = append(items, Item{Key: key, Value: value}) + return false + }) + } else { + p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + items = append(items, Item{Key: key, Value: value}) + return false + }) + } page.Items = items page.PageNumber = pageNumber @@ -115,8 +125,8 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { return p.GetPageWithSize(pageNumber, pageSize), nil } -// UI generates the Markdown UI for the page selector. -func (p *Page) Selector() string { +// Picker generates the Markdown UI for the page Picker +func (p *Page) Picker() string { pageNumber := p.PageNumber pageNumber = max(pageNumber, 1) 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 da4680db8c7..9869924e5b5 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -18,34 +18,67 @@ func TestPager_GetPage(t *testing.T) { tree.Set("d", 4) tree.Set("e", 5) - // Create a new pager. - pager := NewPager(tree, 10) + t.Run("normal ordering", func(t *testing.T) { + // Create a new pager. + pager := NewPager(tree, 10, false) + + // 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{}}, + } - // 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 := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) - for _, tt := range tests { - page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) + uassert.Equal(t, len(tt.expected), len(page.Items)) - uassert.Equal(t, len(tt.expected), len(page.Items)) + for i, item := range page.Items { + uassert.Equal(t, tt.expected[i].Key, item.Key) + uassert.Equal(t, tt.expected[i].Value, item.Value) + } + } + }) + + t.Run("reversed ordering", func(t *testing.T) { + // Create a new pager. + pager := NewPager(tree, 10, true) + + // Define test cases. + tests := []struct { + pageNumber int + pageSize int + expected []Item + }{ + {1, 2, []Item{{Key: "e", Value: 5}, {Key: "d", Value: 4}}}, + {2, 2, []Item{{Key: "c", Value: 3}, {Key: "b", Value: 2}}}, + {3, 2, []Item{{Key: "a", Value: 1}}}, + {1, 3, []Item{{Key: "e", Value: 5}, {Key: "d", Value: 4}, {Key: "c", Value: 3}}}, + {2, 3, []Item{{Key: "b", Value: 2}, {Key: "a", Value: 1}}}, + {1, 5, []Item{{Key: "e", Value: 5}, {Key: "d", Value: 4}, {Key: "c", Value: 3}, {Key: "b", Value: 2}, {Key: "a", Value: 1}}}, + {2, 5, []Item{}}, + } - for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key) - uassert.Equal(t, tt.expected[i].Value, item.Value) + for _, tt := range tests { + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) + + uassert.Equal(t, len(tt.expected), len(page.Items)) + + for i, item := range page.Items { + uassert.Equal(t, tt.expected[i].Key, item.Key) + uassert.Equal(t, tt.expected[i].Value, item.Value) + } } - } + }) } func TestPager_GetPageByPath(t *testing.T) { @@ -56,7 +89,7 @@ func TestPager_GetPageByPath(t *testing.T) { } // Create a new pager. - pager := NewPager(tree, 10) + pager := NewPager(tree, 10, false) // Define test cases. tests := []struct { @@ -80,7 +113,7 @@ func TestPager_GetPageByPath(t *testing.T) { } } -func TestPage_Selector(t *testing.T) { +func TestPage_Picker(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -90,7 +123,7 @@ func TestPage_Selector(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree, 10) + pager := NewPager(tree, 10, false) // Define test cases. tests := []struct { @@ -106,7 +139,7 @@ func TestPage_Selector(t *testing.T) { for _, tt := range tests { page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) - ui := page.Selector() + ui := page.Picker() uassert.Equal(t, tt.expected, ui) } } @@ -119,7 +152,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { } // Create a new pager. - pager := NewPager(tree, 10) + pager := NewPager(tree, 10, false) // Define test cases for a large number of pages. tests := []struct { @@ -145,7 +178,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { for _, tt := range tests { page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) - ui := page.Selector() + ui := page.Picker() uassert.Equal(t, tt.expected, ui) } } @@ -160,7 +193,7 @@ func TestPager_ParseQuery(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree, 10) + pager := NewPager(tree, 10, false) // 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 17029f57861..6342888d6b4 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -16,7 +16,7 @@ func main() { } // Create a new pager. - pager := pager.NewPager(tree, 7) + pager := pager.NewPager(tree, 7, false) for pn := -1; pn < 8; pn++ { page := pager.GetPage(pn) @@ -25,7 +25,7 @@ func main() { for idx, item := range page.Items { println(ufmt.Sprintf("- idx=%d key=%s value=%d", idx, item.Key, item.Value)) } - println(page.Selector()) + println(page.Picker()) println() } } diff --git a/examples/gno.land/r/demo/userbook/render.gno b/examples/gno.land/r/demo/userbook/render.gno index 22d7f97eabd..94f7567cbf4 100644 --- a/examples/gno.land/r/demo/userbook/render.gno +++ b/examples/gno.land/r/demo/userbook/render.gno @@ -2,16 +2,19 @@ package userbook import ( - "sort" "strconv" + "gno.land/r/demo/users" + "gno.land/p/demo/avl/pager" "gno.land/p/demo/ufmt" "gno.land/p/moul/txlink" ) +const usersLink = "/r/demo/users" + func Render(path string) string { - p := pager.NewPager(signupsTree, 2) + p := pager.NewPager(signupsTree, 20, true) page := p.MustGetPageByPath(path) out := "# Welcome to UserBook!\n\n" @@ -19,33 +22,19 @@ func Render(path string) string { out += ufmt.Sprintf("## [Click here to sign up!](%s)\n\n", txlink.Call("SignUp")) out += "---\n\n" - var sorted sortedSignups for _, item := range page.Items { - sorted = append(sorted, item.Value.(*Signup)) - } + signup := item.Value.(*Signup) + user := signup.address.String() - sort.Sort(sorted) + if data := users.GetUserByAddress(signup.address); data != nil { + user = ufmt.Sprintf("[%s](%s:%s)", data.Name, usersLink, data.Name) + } - for _, item := range sorted { - out += ufmt.Sprintf("- **User #%d - %s - signed up on %s**\n\n", item.ordinal, item.address.String(), item.timestamp.Format("02-01-2006 15:04:05")) + out += ufmt.Sprintf("- **User #%d - %s - signed up on %s**\n\n", signup.ordinal, user, signup.timestamp.Format("January 2 2006, 03:04:04 PM")) } out += "---\n\n" out += "**Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "**\n\n" - out += page.Selector() // Repeat selector for ease of navigation + out += page.Picker() return out } - -type sortedSignups []*Signup - -func (s sortedSignups) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s sortedSignups) Len() int { - return len(s) -} - -func (s sortedSignups) Less(i, j int) bool { - return s[i].timestamp.Before(s[j].timestamp) -} diff --git a/examples/gno.land/r/demo/userbook/userbook.gno b/examples/gno.land/r/demo/userbook/userbook.gno index c958dc9e5b0..03027f064b0 100644 --- a/examples/gno.land/r/demo/userbook/userbook.gno +++ b/examples/gno.land/r/demo/userbook/userbook.gno @@ -6,6 +6,7 @@ import ( "time" "gno.land/p/demo/avl" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) @@ -15,7 +16,11 @@ type Signup struct { timestamp time.Time } -var signupsTree = avl.NewTree() +var ( + signupsTree = avl.NewTree() + tracker = avl.NewTree() + idCounter seqid.ID +) const signUpEvent = "SignUp" @@ -28,19 +33,22 @@ func SignUp() string { caller := std.PrevRealm().Addr() // Check if the user is already signed up - if _, exists := signupsTree.Get(caller.String()); exists { - panic(caller + " is already signed up!") + if _, exists := tracker.Get(caller.String()); exists { + panic(caller.String() + " is already signed up!") } now := time.Now() + // Sign up the user - signupsTree.Set(caller.String(), &Signup{ - std.PrevRealm().Addr(), + signupsTree.Set(idCounter.Next().String(), &Signup{ + caller, signupsTree.Size(), now, }) - std.Emit(signUpEvent, "SignedUpAccount", caller.String()) + tracker.Set(caller.String(), struct{}{}) + + std.Emit(signUpEvent, "account", caller.String()) return ufmt.Sprintf("%s added to userbook! Timestamp: %s", caller.String(), now.Format(time.RFC822Z)) } diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 1f08c9ae08c..8547a6e60e0 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,14 +328,14 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - page := pager.NewPager(&name2User, 50).MustGetPageByPath(path) + page := pager.NewPager(&name2User, 50, false).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() + doc += page.Picker() return doc } diff --git a/examples/gno.land/r/docs/avl_pager/avl_pager.gno b/examples/gno.land/r/docs/avl_pager/avl_pager.gno index 75807b71981..af8a6a10b48 100644 --- a/examples/gno.land/r/docs/avl_pager/avl_pager.gno +++ b/examples/gno.land/r/docs/avl_pager/avl_pager.gno @@ -22,19 +22,19 @@ func init() { // Render paginated content based on the given URL path. // URL format: `...?page=&size=` (default is page 1 and size 10). func Render(path string) string { - p := pager.NewPager(tree, 10) // Default page size is 10 + p := pager.NewPager(tree, 10, false) // Default page size is 10 page := p.MustGetPageByPath(path) // Header and pagination info result := "# Paginated Items\n" result += "Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "\n\n" - result += page.Selector() + "\n\n" + result += page.Picker() + "\n\n" // Display items on the current page for _, item := range page.Items { result += "- " + item.Key + ": " + item.Value.(string) + "\n" } - result += "\n" + page.Selector() // Repeat selector for ease of navigation + result += "\n" + page.Picker() // Repeat page picker for ease of navigation return result } diff --git a/examples/gno.land/r/leon/hof/render.gno b/examples/gno.land/r/leon/hof/render.gno index b4d51d03362..0721c7d6e72 100644 --- a/examples/gno.land/r/leon/hof/render.gno +++ b/examples/gno.land/r/leon/hof/render.gno @@ -38,7 +38,7 @@ func (e Exhibition) Render(path string, dashboard bool) string { out += "
\n\n" - page := pager.NewPager(e.itemsSorted, pageSize).MustGetPageByPath(path) + page := pager.NewPager(e.itemsSorted, pageSize, false).MustGetPageByPath(path) for i := len(page.Items) - 1; i >= 0; i-- { item := page.Items[i] @@ -52,7 +52,7 @@ func (e Exhibition) Render(path string, dashboard bool) string { out += "
\n\n" - out += page.Selector() + out += page.Picker() return out }