Skip to content

Commit

Permalink
feat(examples): add avl_pager reverse option, update userbook (#3297)
Browse files Browse the repository at this point in the history
<!-- please provide a detailed description of the changes made in this
pull request. -->

## Description

This PR adds the reverse option in the avl_pager package, and updates
the rendering in the `r/demo/userbook` realm.

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] Provided any useful hints for running manual tests
</details>
  • Loading branch information
leohhhn authored Dec 8, 2024
1 parent 61a5c02 commit 052a2a1
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 75 deletions.
24 changes: 17 additions & 7 deletions examples/gno.land/p/demo/avl/pager/pager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Pager struct {
PageQueryParam string
SizeQueryParam string
DefaultPageSize int
Reversed bool
}

// Page represents a single page of results.
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down
93 changes: 63 additions & 30 deletions examples/gno.land/p/demo/avl/pager/pager_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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 {
Expand All @@ -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)
}
}
Expand All @@ -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 {
Expand All @@ -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)
}
}
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions examples/gno.land/p/demo/avl/pager/z_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
}
}
Expand Down
35 changes: 12 additions & 23 deletions examples/gno.land/r/demo/userbook/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,39 @@
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"

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)
}
20 changes: 14 additions & 6 deletions examples/gno.land/r/demo/userbook/userbook.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"gno.land/p/demo/avl"
"gno.land/p/demo/seqid"
"gno.land/p/demo/ufmt"
)

Expand All @@ -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"

Expand All @@ -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))
}
4 changes: 2 additions & 2 deletions examples/gno.land/r/demo/users/users.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions examples/gno.land/r/docs/avl_pager/avl_pager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ func init() {
// Render paginated content based on the given URL path.
// URL format: `...?page=<page>&size=<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
}
4 changes: 2 additions & 2 deletions examples/gno.land/r/leon/hof/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (e Exhibition) Render(path string, dashboard bool) string {

out += "<div class='columns-2'>\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]
Expand All @@ -52,7 +52,7 @@ func (e Exhibition) Render(path string, dashboard bool) string {

out += "</div><!-- /columns-2 -->\n\n"

out += page.Selector()
out += page.Picker()

return out
}
Expand Down

0 comments on commit 052a2a1

Please sign in to comment.