github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/r/demo/userbook/userbook.gno (about)

     1  // This realm demonstrates a small userbook system working with gnoweb
     2  package userbook
     3  
     4  import (
     5  	"std"
     6  	"strconv"
     7  
     8  	"gno.land/p/demo/avl"
     9  	"gno.land/p/demo/mux"
    10  	"gno.land/p/demo/ufmt"
    11  )
    12  
    13  type Signup struct {
    14  	account string
    15  	height  int64
    16  }
    17  
    18  // signups - keep a slice of signed up addresses efficient pagination
    19  var signups []Signup
    20  
    21  // tracker - keep track of who signed up
    22  var (
    23  	tracker *avl.Tree
    24  	router  *mux.Router
    25  )
    26  
    27  const (
    28  	defaultPageSize = 20
    29  	pathArgument    = "number"
    30  	subPath         = "page/{" + pathArgument + "}"
    31  )
    32  
    33  func init() {
    34  	// Set up tracker tree
    35  	tracker = avl.NewTree()
    36  
    37  	// Set up route handling
    38  	router = mux.NewRouter()
    39  	router.HandleFunc("", renderHelper)
    40  	router.HandleFunc(subPath, renderHelper)
    41  
    42  	// Sign up the deployer
    43  	SignUp()
    44  }
    45  
    46  func SignUp() string {
    47  	// Get transaction caller
    48  	caller := std.PrevRealm().Addr().String()
    49  	height := std.GetHeight()
    50  
    51  	if _, exists := tracker.Get(caller); exists {
    52  		panic(caller + " is already signed up!")
    53  	}
    54  
    55  	tracker.Set(caller, struct{}{})
    56  	signup := Signup{
    57  		caller,
    58  		height,
    59  	}
    60  
    61  	signups = append(signups, signup)
    62  	return ufmt.Sprintf("%s added to userbook up at block #%d!", signup.account, signup.height)
    63  }
    64  
    65  func GetSignupsInRange(page, pageSize int) ([]Signup, int) {
    66  	if page < 1 {
    67  		panic("page number cannot be less than 1")
    68  	}
    69  
    70  	if pageSize < 1 || pageSize > 50 {
    71  		panic("page size must be from 1 to 50")
    72  	}
    73  
    74  	// Pagination
    75  	// Calculate indexes
    76  	startIndex := (page - 1) * pageSize
    77  	endIndex := startIndex + pageSize
    78  
    79  	// If page does not contain any users
    80  	if startIndex >= len(signups) {
    81  		return nil, -1
    82  	}
    83  
    84  	// If page contains fewer users than the page size
    85  	if endIndex > len(signups) {
    86  		endIndex = len(signups)
    87  	}
    88  
    89  	return signups[startIndex:endIndex], endIndex
    90  }
    91  
    92  func renderHelper(res *mux.ResponseWriter, req *mux.Request) {
    93  	totalSignups := len(signups)
    94  	res.Write("# Welcome to UserBook!\n\n")
    95  
    96  	// Get URL parameter
    97  	page, err := strconv.Atoi(req.GetVar("number"))
    98  	if err != nil {
    99  		page = 1 // render first page on bad input
   100  	}
   101  
   102  	// Fetch paginated signups
   103  	fetchedSignups, endIndex := GetSignupsInRange(page, defaultPageSize)
   104  	// Handle empty page case
   105  	if len(fetchedSignups) == 0 {
   106  		res.Write("No users on this page!\n\n")
   107  		res.Write("---\n\n")
   108  		res.Write("[Back to Page #1](/r/demo/userbook:page/1)\n\n")
   109  		return
   110  	}
   111  
   112  	// Write page title
   113  	res.Write(ufmt.Sprintf("## UserBook - Page #%d:\n\n", page))
   114  
   115  	// Write signups
   116  	pageStartIndex := defaultPageSize * (page - 1)
   117  	for i, signup := range fetchedSignups {
   118  		out := ufmt.Sprintf("#### User #%d - %s - signed up at Block #%d\n", pageStartIndex+i, signup.account, signup.height)
   119  		res.Write(out)
   120  	}
   121  
   122  	res.Write("---\n\n")
   123  
   124  	// Write UserBook info
   125  	latestSignupIndex := totalSignups - 1
   126  	res.Write(ufmt.Sprintf("#### Total users: %d\n", totalSignups))
   127  	res.Write(ufmt.Sprintf("#### Latest signup: User #%d at Block #%d\n", latestSignupIndex, signups[latestSignupIndex].height))
   128  
   129  	res.Write("---\n\n")
   130  
   131  	// Write page number
   132  	res.Write(ufmt.Sprintf("You're viewing page #%d", page))
   133  
   134  	// Write navigation buttons
   135  	var prevPage string
   136  	var nextPage string
   137  	// If we are on any page that is not the first page
   138  	if page > 1 {
   139  		prevPage = ufmt.Sprintf(" - [Previous page](/r/demo/userbook:page/%d)", page-1)
   140  	}
   141  
   142  	// If there are more pages after the current one
   143  	if endIndex < totalSignups {
   144  		nextPage = ufmt.Sprintf(" - [Next page](/r/demo/userbook:page/%d)\n\n", page+1)
   145  	}
   146  
   147  	res.Write(prevPage)
   148  	res.Write(nextPage)
   149  }
   150  
   151  func Render(path string) string {
   152  	return router.Render(path)
   153  }