Skip to content

Commit

Permalink
chore(examples): update userbook example to use avl_pager (#3251)
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

Updates the `r/demo/userbook` example to use the `avl_pager` package.

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

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
</details>

---------

Co-authored-by: Morgan <[email protected]>
  • Loading branch information
leohhhn and thehowl authored Dec 7, 2024
1 parent 7278687 commit 918c9ab
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 209 deletions.
51 changes: 51 additions & 0 deletions examples/gno.land/r/demo/userbook/render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Package userbook demonstrates a small userbook system working with gnoweb
package userbook

import (
"sort"
"strconv"

"gno.land/p/demo/avl/pager"
"gno.land/p/demo/ufmt"
"gno.land/p/moul/txlink"
)

func Render(path string) string {
p := pager.NewPager(signupsTree, 2)
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))
}

sort.Sort(sorted)

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 += "---\n\n"
out += "**Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "**\n\n"
out += page.Selector() // Repeat selector for ease of navigation
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)
}
148 changes: 18 additions & 130 deletions examples/gno.land/r/demo/userbook/userbook.gno
Original file line number Diff line number Diff line change
@@ -1,158 +1,46 @@
// This realm demonstrates a small userbook system working with gnoweb
// Package userbook demonstrates a small userbook system working with gnoweb
package userbook

import (
"std"
"strconv"
"time"

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

type Signup struct {
account string
height int64
address std.Address
ordinal int
timestamp time.Time
}

// signups - keep a slice of signed up addresses efficient pagination
var signups []Signup
var signupsTree = avl.NewTree()

// tracker - keep track of who signed up
var (
tracker *avl.Tree
router *mux.Router
)

const (
defaultPageSize = 20
pathArgument = "number"
subPath = "page/{" + pathArgument + "}"
signUpEvent = "SignUp"
)
const signUpEvent = "SignUp"

func init() {
// Set up tracker tree
tracker = avl.NewTree()

// Set up route handling
router = mux.NewRouter()
router.HandleFunc("", renderHelper)
router.HandleFunc(subPath, renderHelper)

// Sign up the deployer
SignUp()
SignUp() // Sign up the deployer
}

func SignUp() string {
// Get transaction caller
caller := std.PrevRealm().Addr().String()
height := std.GetHeight()
caller := std.PrevRealm().Addr()

// Check if the user is already signed up
if _, exists := tracker.Get(caller); exists {
if _, exists := signupsTree.Get(caller.String()); exists {
panic(caller + " is already signed up!")
}

now := time.Now()
// Sign up the user
tracker.Set(caller, struct{}{})
signup := Signup{
caller,
height,
}

signups = append(signups, signup)
std.Emit(signUpEvent, "SignedUpAccount", signup.account)
signupsTree.Set(caller.String(), &Signup{
std.PrevRealm().Addr(),
signupsTree.Size(),
now,
})

return ufmt.Sprintf("%s added to userbook up at block #%d!", signup.account, signup.height)
}

func GetSignupsInRange(page, pageSize int) ([]Signup, int) {
if page < 1 {
panic("page number cannot be less than 1")
}

if pageSize < 1 || pageSize > 50 {
panic("page size must be from 1 to 50")
}

// Pagination
// Calculate indexes
startIndex := (page - 1) * pageSize
endIndex := startIndex + pageSize

// If page does not contain any users
if startIndex >= len(signups) {
return nil, -1
}

// If page contains fewer users than the page size
if endIndex > len(signups) {
endIndex = len(signups)
}

return signups[startIndex:endIndex], endIndex
}

func renderHelper(res *mux.ResponseWriter, req *mux.Request) {
totalSignups := len(signups)
res.Write("# Welcome to UserBook!\n\n")

// Get URL parameter
page, err := strconv.Atoi(req.GetVar("number"))
if err != nil {
page = 1 // render first page on bad input
}

// Fetch paginated signups
fetchedSignups, endIndex := GetSignupsInRange(page, defaultPageSize)
// Handle empty page case
if len(fetchedSignups) == 0 {
res.Write("No users on this page!\n\n")
res.Write("---\n\n")
res.Write("[Back to Page #1](/r/demo/userbook:page/1)\n\n")
return
}

// Write page title
res.Write(ufmt.Sprintf("## UserBook - Page #%d:\n\n", page))

// Write signups
pageStartIndex := defaultPageSize * (page - 1)
for i, signup := range fetchedSignups {
out := ufmt.Sprintf("#### User #%d - %s - signed up at Block #%d\n", pageStartIndex+i, signup.account, signup.height)
res.Write(out)
}

res.Write("---\n\n")

// Write UserBook info
latestSignupIndex := totalSignups - 1
res.Write(ufmt.Sprintf("#### Total users: %d\n", totalSignups))
res.Write(ufmt.Sprintf("#### Latest signup: User #%d at Block #%d\n", latestSignupIndex, signups[latestSignupIndex].height))

res.Write("---\n\n")

// Write page number
res.Write(ufmt.Sprintf("You're viewing page #%d", page))

// Write navigation buttons
var prevPage string
var nextPage string
// If we are on any page that is not the first page
if page > 1 {
prevPage = ufmt.Sprintf(" - [Previous page](/r/demo/userbook:page/%d)", page-1)
}

// If there are more pages after the current one
if endIndex < totalSignups {
nextPage = ufmt.Sprintf(" - [Next page](/r/demo/userbook:page/%d)\n\n", page+1)
}

res.Write(prevPage)
res.Write(nextPage)
}
std.Emit(signUpEvent, "SignedUpAccount", caller.String())

func Render(path string) string {
return router.Render(path)
return ufmt.Sprintf("%s added to userbook! Timestamp: %s", caller.String(), now.Format(time.RFC822Z))
}
79 changes: 0 additions & 79 deletions examples/gno.land/r/demo/userbook/userbook_test.gno

This file was deleted.

0 comments on commit 918c9ab

Please sign in to comment.