Skip to content

Commit

Permalink
Merge branch 'master' into feat/token-hub
Browse files Browse the repository at this point in the history
  • Loading branch information
matijamarjanovic authored Jan 31, 2025
2 parents cbfe597 + 57da324 commit 1c3b939
Show file tree
Hide file tree
Showing 26 changed files with 545 additions and 124 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/gnoland.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ on:
# Changes to examples/ can create failures in gno.land, eg. txtars,
# see: https://github.com/gnolang/gno/pull/3590
- examples/**
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/gnovm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
paths:
- gnovm/**
- tm2/** # GnoVM has a dependency on TM2 types
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/tm2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:
pull_request:
paths:
- tm2/**
# We trigger the testing workflow for changes to the main go.mod,
# since this can affect test results
- go.mod
workflow_dispatch:

jobs:
Expand Down
1 change: 1 addition & 0 deletions examples/gno.land/r/docs/avl_pager_with_params/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/docs/avl_pager_params
86 changes: 86 additions & 0 deletions examples/gno.land/r/docs/avl_pager_with_params/render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package avl_pager_params

import (
"gno.land/p/demo/avl"
"gno.land/p/demo/avl/pager"
"gno.land/p/demo/seqid"
"gno.land/p/demo/ufmt"
"gno.land/p/moul/realmpath"
)

// We'll keep some demo data in an AVL tree to showcase pagination.
var (
items *avl.Tree
idCounter seqid.ID
)

func init() {
items = avl.NewTree()
// Populate the tree with 15 sample items for demonstration.
for i := 1; i <= 15; i++ {
id := idCounter.Next().String()
items.Set(id, "Some item value: "+id)
}
}

func Render(path string) string {
// 1) Parse the incoming path to split route vs. query.
req := realmpath.Parse(path)
// - req.Path contains everything *before* ? or $ (? - query params, $ - gnoweb params)
// - The remaining part (page=2, size=5, etc.) is not in req.Path.

// 2) If no specific route is provided (req.Path == ""), we’ll show a “home” page
// that displays a list of configs in paginated form.
if req.Path == "" {
return renderHome(path)
}

// 3) If a route *is* provided (e.g. :SomeKey),
// we will interpret it as a request for a specific page.
return renderConfigItem(req.Path)
}

// renderHome shows a paginated list of config items if route == "".
func renderHome(fullPath string) string {
// Create a Pager for our config tree, with a default page size of 5.
p := pager.NewPager(items, 5, false)

// MustGetPageByPath uses the *entire* path (including query parts: ?page=2, etc.)
page := p.MustGetPageByPath(fullPath)

// Start building the output (plain text or markdown).
out := "# AVL Pager + Render paths\n\n"
out += `This realm showcases how to maintain a paginated list while properly parsing render paths.
You can see how a single page can include a paginated element (like the example below), and how clicking
an item can take you to a dedicated page for that specific item.
No matter how you browse through the paginated list, the introductory text (this section) remains the same.
`

out += ufmt.Sprintf("Showing page %d of %d\n\n", page.PageNumber, page.TotalPages)

// List items for this page.
for _, item := range page.Items {
// Link each item to a details page: e.g. ":Config01"
out += ufmt.Sprintf("- [Item %s](/r/docs/avl_pager_params:%s)\n", item.Key, item.Key)
}

// Insert pagination controls (previous/next links, etc.).
out += "\n" + page.Picker() + "\n\n"
out += "### [Go back to r/docs](/r/docs)"

return out
}

// renderConfigItem shows details for a single item, e.g. ":item001".
func renderConfigItem(itemName string) string {
value, ok := items.Get(itemName)
if !ok {
return ufmt.Sprintf("**No item found** for key: %s", itemName)
}

out := ufmt.Sprintf("# Item %s\n\n%s\n\n", itemName, value.(string))
out += "[Go back](/r/docs/avl_pager_params)"
return out
}
2 changes: 2 additions & 0 deletions examples/gno.land/r/docs/docs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Explore various examples to learn more about Gno functionality and usage.
- [Source](/r/docs/source) - View realm source code.
- [Buttons](/r/docs/buttons) - Add buttons to your realm's render.
- [AVL Pager](/r/docs/avl_pager) - Paginate through AVL tree items.
- [AVL Pager + Render paths](/r/docs/avl_pager_params) - Handle render arguments with pagination.
- [Img Embed](/r/docs/img_embed) - Demonstrates how to embed an image.
- [Optional Render](/r/docs/optional_render) - Render() is optional in realms.
- ...
<!-- meta issue with suggestions: https://github.com/gnolang/gno/issues/3292 -->
Expand Down
1 change: 1 addition & 0 deletions examples/gno.land/r/docs/optional_render/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/r/docs/optional_render
7 changes: 7 additions & 0 deletions examples/gno.land/r/docs/optional_render/optional_render.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package optional_render

func Info() string {
return `Having a Render() function in your realm is optional!
If you do decide to have a Render() function, it must have the following signature:
func Render(path string) string { ... }`
}
119 changes: 87 additions & 32 deletions examples/gno.land/r/leon/config/config.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,116 @@ package config
import (
"errors"
"std"
"strconv"
"strings"
"time"

"gno.land/p/demo/avl"
p "gno.land/p/demo/avl/pager"
"gno.land/p/demo/ownable"
"gno.land/p/demo/ufmt"
"gno.land/p/moul/md"
"gno.land/p/moul/realmpath"
)

var (
main std.Address // leon's main address
backup std.Address // backup address
configs = avl.NewTree()
pager = p.NewPager(configs, 10, false)
banner = "---\n[[Leon's Home page]](/r/leon/home) | [[GitHub: @leohhhn]](https://github.com/leohhhn)\n\n---"
absPath = strings.TrimPrefix(std.CurrentRealm().PkgPath(), std.GetChainDomain())

// SafeObjects
OwnableMain = ownable.NewWithAddress("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")
OwnableBackup = ownable.NewWithAddress("g1lavlav7zwsjqlzzl3qdl3nl242qtf638vnhdjh")

ErrInvalidAddr = errors.New("leon's config: invalid address")
ErrUnauthorized = errors.New("leon's config: unauthorized")
)

func init() {
main = "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5"
type Config struct {
lines string
updated time.Time
}

func Address() std.Address {
return main
}
func AddConfig(name, lines string) {
if !IsAuthorized(std.PrevRealm().Addr()) {
panic(ErrUnauthorized)
}

func Backup() std.Address {
return backup
configs.Set(name, Config{
lines: lines,
updated: time.Now(),
}) // no overwrite check
}

func SetAddress(a std.Address) error {
if !a.IsValid() {
return ErrInvalidAddr
func RemoveConfig(name string) {
if !IsAuthorized(std.PrevRealm().Addr()) {
panic(ErrUnauthorized)
}

if err := checkAuthorized(); err != nil {
return err
if _, ok := configs.Remove(name); !ok {
panic("no config with that name")
}

main = a
return nil
}

func SetBackup(a std.Address) error {
if !a.IsValid() {
return ErrInvalidAddr
func UpdateBanner(newBanner string) {
if !IsAuthorized(std.PrevRealm().Addr()) {
panic(ErrUnauthorized)
}

if err := checkAuthorized(); err != nil {
return err
}
banner = newBanner
}

backup = a
return nil
func IsAuthorized(addr std.Address) bool {
return addr == OwnableMain.Owner() || addr == OwnableBackup.Owner()
}

func checkAuthorized() error {
caller := std.PrevRealm().Addr()
isAuthorized := caller == main || caller == backup
func Banner() string {
return banner
}

func Render(path string) (out string) {
req := realmpath.Parse(path)
if req.Path == "" {
out += md.H1("Leon's config package")

out += ufmt.Sprintf("Leon's main address: %s\n\n", OwnableMain.Owner().String())
out += ufmt.Sprintf("Leon's backup address: %s\n\n", OwnableBackup.Owner().String())

if !isAuthorized {
return ErrUnauthorized
out += md.H2("Leon's configs")

if configs.Size() == 0 {
out += "No configs yet :c\n\n"
}

page := pager.MustGetPageByPath(path)
for _, item := range page.Items {
out += ufmt.Sprintf("- [%s](%s:%s)\n\n", item.Key, absPath, item.Key)
}

out += page.Picker()
out += "\n\n"
out += "Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "\n\n"

out += Banner()

return out
}

return nil
return renderConfPage(req.Path)
}

func renderConfPage(confName string) (out string) {
raw, ok := configs.Get(confName)
if !ok {
out += md.H1("404")
out += "That config does not exist :/"
return out
}

conf := raw.(Config)
out += md.H1(confName)
out += ufmt.Sprintf("```\n%s\n```\n\n", conf.lines)
out += ufmt.Sprintf("_Last updated on %s_", conf.updated.Format("02 Jan, 2006"))

return out
}
22 changes: 12 additions & 10 deletions examples/gno.land/r/leon/hof/hof.gno
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"gno.land/p/demo/ownable"
"gno.land/p/demo/pausable"
"gno.land/p/demo/seqid"

"gno.land/r/leon/config"
)

var (
Expand All @@ -24,7 +26,7 @@ type (
Exhibition struct {
itemCounter seqid.ID
description string
items *avl.Tree // pkgPath > Item
items *avl.Tree // pkgPath > &Item
itemsSorted *avl.Tree // same data but sorted, storing pointers
}

Expand All @@ -43,7 +45,7 @@ func init() {
itemsSorted: avl.NewTree(),
}

Ownable = ownable.NewWithAddress(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5"))
Ownable = ownable.NewWithAddress(config.OwnableMain.Owner()) // OrigSendOwnable?
Pausable = pausable.NewFromOwnable(Ownable)
}

Expand Down Expand Up @@ -85,14 +87,14 @@ func Register() {
func Upvote(pkgpath string) {
rawItem, ok := exhibition.items.Get(pkgpath)
if !ok {
panic(ErrNoSuchItem.Error())
panic(ErrNoSuchItem)
}

item := rawItem.(*Item)
caller := std.PrevRealm().Addr().String()

if item.upvote.Has(caller) {
panic(ErrDoubleUpvote.Error())
panic(ErrDoubleUpvote)
}

item.upvote.Set(caller, struct{}{})
Expand All @@ -101,34 +103,34 @@ func Upvote(pkgpath string) {
func Downvote(pkgpath string) {
rawItem, ok := exhibition.items.Get(pkgpath)
if !ok {
panic(ErrNoSuchItem.Error())
panic(ErrNoSuchItem)
}

item := rawItem.(*Item)
caller := std.PrevRealm().Addr().String()

if item.downvote.Has(caller) {
panic(ErrDoubleDownvote.Error())
panic(ErrDoubleDownvote)
}

item.downvote.Set(caller, struct{}{})
}

func Delete(pkgpath string) {
if !Ownable.CallerIsOwner() {
panic(ownable.ErrUnauthorized.Error())
panic(ownable.ErrUnauthorized)
}

i, ok := exhibition.items.Get(pkgpath)
if !ok {
panic(ErrNoSuchItem.Error())
panic(ErrNoSuchItem)
}

if _, removed := exhibition.itemsSorted.Remove(i.(*Item).id.String()); !removed {
panic(ErrNoSuchItem.Error())
panic(ErrNoSuchItem)
}

if _, removed := exhibition.items.Remove(pkgpath); !removed {
panic(ErrNoSuchItem.Error())
panic(ErrNoSuchItem)
}
}
Loading

0 comments on commit 1c3b939

Please sign in to comment.