From 269ec832f08b101664eac17781a1c7621c34a664 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 17 Jun 2024 17:43:58 +0200 Subject: [PATCH 01/60] rm --- examples/gno.land/r/gnoland/events/events.gno | 240 ------------------ .../r/gnoland/events/events_filetest.gno | 226 ----------------- 2 files changed, 466 deletions(-) delete mode 100644 examples/gno.land/r/gnoland/events/events.gno delete mode 100644 examples/gno.land/r/gnoland/events/events_filetest.gno diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno deleted file mode 100644 index 288a7531835..00000000000 --- a/examples/gno.land/r/gnoland/events/events.gno +++ /dev/null @@ -1,240 +0,0 @@ -package events - -import ( - "gno.land/p/demo/ui" -) - -// XXX: p/demo/ui API is crappy, we need to make it more idiomatic -// XXX: use an updatable block system to update content from a DAO -// XXX: var blocks avl.Tree - -func Render(_ string) string { - dom := ui.DOM{Prefix: "r/gnoland/events:"} - dom.Title = "Gno.land Core Team Attends Industry Events & Meetups" - dom.Classes = []string{"gno-tmpl-section"} - - // body - dom.Body.Append(introSection()...) - dom.Body.Append(ui.HR{}) - dom.Body.Append(upcomingEvents()...) - dom.Body.Append(ui.HR{}) - dom.Body.Append(pastEvents()...) - - return dom.String() -} - -func introSection() ui.Element { - return ui.Element{ - ui.Paragraph("If you’re interested in building web3 with us, catch up with Gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform."), - } -} - -func upcomingEvents() ui.Element { - return ui.Element{ - ui.H2("Upcoming Events"), - ui.Text(`
-
- -### GopherCon EU -- Come Meet Us at our Booth -- Berlin, June 17 - 20, 2024 - -[Learn More](https://gophercon.eu/) -
- -
- -### GopherCon US -- Come Meet Us at our Booth -- Chicago, July 7 - 10, 2024 - -[Learn More](https://www.gophercon.com/) - -
- -
- -### Nebular Summit -- Join our workshop -- Brussels, July 12 - 13, 2024 - -[Learn More](https://nebular.builders/) -
- -
- -
-
- -
-
`), - } -} - -func pastEvents() ui.Element { - return ui.Element{ - ui.H2("Past Events"), - ui.Text(`
- -
- -### Gno @ Golang Serbia - -- **Join the meetup** -- Belgrade, May 23, 2024 - -[Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia) - -
- -
- -### Intro to Gno Tokyo - -- **Join the meetup** -- Tokyo, April 11, 2024 - -[Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo) - -
- -
- -### Go to Gno Seoul - -- **Join the workshop** -- Seoul, March 23, 2024 - -[Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620) - -
- -
- -### GopherCon US - -- **Come Meet Us at our Booth** -- San Diego, September 26 - 29, 2023 - -[Learn more](https://www.gophercon.com/) - -
- -
- -### GopherCon EU - -- **Come Meet Us at our Booth** -- Berlin, July 26 - 29, 2023 - -[Learn more](https://gophercon.eu/) - -
- -
- -### Nebular Summit Gno.land for Developers - -- Paris, July 24 - 25, 2023 -- Manfred Touron - -[Learn more](https://www.nebular.builders/) - -
- -
- -### EthCC - -- **Come Meet Us at our Booth** -- Paris, July 17 - 20, 2023 -- Manfred Touron - -[Learn more](https://www.ethcc.io/) - -
- -
- -### Eth Seoul - -- **The Evolution of Smart Contracts: A Journey into Gno.land** -- Seoul, June 3, 2023 -- Manfred Touron - -[Learn more](https://2023.ethseoul.org/) - -
-
- -### BUIDL Asia - -- **Proof of Contribution in Gno.land** -- Seoul, June 6, 2023 -- Manfred Touron - -[Learn more](https://www.buidl.asia/) - -
-
- -### Game Developer Conference - -- **Side Event: Web3 Gaming Apps Powered by Gno** -- San Francisco, Mach 23, 2023 -- Jae Kwon - -[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) - -
-
- -### EthDenver - -- **Side Event: Discover Gno.land** -- Denver, Feb 24 - Mar 5, 2023 -- Jae Kwon - -[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) - -
-
- -### Istanbul Blockchain Week - -- Istanbul, Nov 14 - 17, 2022 -- Manfred Touron - -[Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4) - -
-
- -### Web Summit Buckle Up and Build with Cosmos - -- Lisbon, Nov 1 - 4, 2022 -- Manfred Touron - -
-
- -### Cosmoverse - -- Medallin, Sept 26 - 28, 2022 -- Manfred Touron - -[Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk) - -
-
- -### Berlin Blockchain Week Buckle Up and Build with Cosmos - -- Berlin, Sept 11 - 18, 2022 - -[Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI) - -
-
`), - } -} diff --git a/examples/gno.land/r/gnoland/events/events_filetest.gno b/examples/gno.land/r/gnoland/events/events_filetest.gno deleted file mode 100644 index e67fc050d3c..00000000000 --- a/examples/gno.land/r/gnoland/events/events_filetest.gno +++ /dev/null @@ -1,226 +0,0 @@ -package main - -import "gno.land/r/gnoland/events" - -func main() { - println(events.Render("")) -} - -// Output: -//
-// -// # Gno.land Core Team Attends Industry Events & Meetups -// -// -// If you’re interested in building web3 with us, catch up with Gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform. -// -// -// --- -// -// ## Upcoming Events -// -//
-//
-// -// ### GopherCon EU -// - Come Meet Us at our Booth -// - Berlin, June 17 - 20, 2024 -// -// [Learn More](https://gophercon.eu/) -//
-// -//
-// -// ### GopherCon US -// - Come Meet Us at our Booth -// - Chicago, July 7 - 10, 2024 -// -// [Learn More](https://www.gophercon.com/) -// -//
-// -//
-// -// ### Nebular Summit -// - Join our workshop -// - Brussels, July 12 - 13, 2024 -// -// [Learn More](https://nebular.builders/) -//
-// -//
-// -//
-//
-// -//
-//
-// -// --- -// -// ## Past Events -// -//
-// -//
-// -// ### Gno @ Golang Serbia -// -// - **Join the meetup** -// - Belgrade, May 23, 2024 -// -// [Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia) -// -//
-// -//
-// -// ### Intro to Gno Tokyo -// -// - **Join the meetup** -// - Tokyo, April 11, 2024 -// -// [Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo) -// -//
-// -//
-// -// ### Go to Gno Seoul -// -// - **Join the workshop** -// - Seoul, March 23, 2024 -// -// [Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620) -// -//
-// -//
-// -// ### GopherCon US -// -// - **Come Meet Us at our Booth** -// - San Diego, September 26 - 29, 2023 -// -// [Learn more](https://www.gophercon.com/) -// -//
-// -//
-// -// ### GopherCon EU -// -// - **Come Meet Us at our Booth** -// - Berlin, July 26 - 29, 2023 -// -// [Learn more](https://gophercon.eu/) -// -//
-// -//
-// -// ### Nebular Summit Gno.land for Developers -// -// - Paris, July 24 - 25, 2023 -// - Manfred Touron -// -// [Learn more](https://www.nebular.builders/) -// -//
-// -//
-// -// ### EthCC -// -// - **Come Meet Us at our Booth** -// - Paris, July 17 - 20, 2023 -// - Manfred Touron -// -// [Learn more](https://www.ethcc.io/) -// -//
-// -//
-// -// ### Eth Seoul -// -// - **The Evolution of Smart Contracts: A Journey into Gno.land** -// - Seoul, June 3, 2023 -// - Manfred Touron -// -// [Learn more](https://2023.ethseoul.org/) -// -//
-//
-// -// ### BUIDL Asia -// -// - **Proof of Contribution in Gno.land** -// - Seoul, June 6, 2023 -// - Manfred Touron -// -// [Learn more](https://www.buidl.asia/) -// -//
-//
-// -// ### Game Developer Conference -// -// - **Side Event: Web3 Gaming Apps Powered by Gno** -// - San Francisco, Mach 23, 2023 -// - Jae Kwon -// -// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) -// -//
-//
-// -// ### EthDenver -// -// - **Side Event: Discover Gno.land** -// - Denver, Feb 24 - Mar 5, 2023 -// - Jae Kwon -// -// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) -// -//
-//
-// -// ### Istanbul Blockchain Week -// -// - Istanbul, Nov 14 - 17, 2022 -// - Manfred Touron -// -// [Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4) -// -//
-//
-// -// ### Web Summit Buckle Up and Build with Cosmos -// -// - Lisbon, Nov 1 - 4, 2022 -// - Manfred Touron -// -//
-//
-// -// ### Cosmoverse -// -// - Medallin, Sept 26 - 28, 2022 -// - Manfred Touron -// -// [Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk) -// -//
-//
-// -// ### Berlin Blockchain Week Buckle Up and Build with Cosmos -// -// - Berlin, Sept 11 - 18, 2022 -// -// [Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI) -// -//
-//
-// -//
From a8f05aa01d3b6369456574146774f39b30f97f89 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 17 Jun 2024 22:43:10 +0200 Subject: [PATCH 02/60] add authorizable, modify ownable, add initial r/events --- .../exts/authorizable/authorizable.gno | 70 ++++++++++++++ .../exts/authorizable/authorizable_test.gno | 50 ++++++++++ .../demo/ownable/exts/authorizable/errors.gno | 8 ++ .../p/demo/ownable/exts/authorizable/gno.mod | 1 + examples/gno.land/p/demo/ownable/ownable.gno | 16 ++-- .../gno.land/p/demo/ownable/ownable_test.gno | 64 ++++++------- examples/gno.land/r/gnoland/events/errors.gno | 8 ++ examples/gno.land/r/gnoland/events/events.go | 91 +++++++++++++++++++ 8 files changed, 271 insertions(+), 37 deletions(-) create mode 100644 examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno create mode 100644 examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno create mode 100644 examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno create mode 100644 examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod create mode 100644 examples/gno.land/r/gnoland/events/errors.gno create mode 100644 examples/gno.land/r/gnoland/events/events.go diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno new file mode 100644 index 00000000000..1b16be80c6d --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -0,0 +1,70 @@ +// Package authorizable is an extension of p/demo/ownable; +// It allows the user to instantiate an Authorizable struct, which extends +// p/demo/ownable with a list of users that are authorized for something. +// By using authorizable, you have a superuser (ownable), as well as another +// authorization level, which can be used for adding moderators or similar to your realm. +package authorizable + +import ( + "std" + + "gno.land/p/demo/ownable" +) + +type Authorizable struct { + *ownable.Ownable + authorized map[std.Address]struct{} +} + +func NewAuthorizable() *Authorizable { + a := &Authorizable{ + ownable.NewOwnable(), + make(map[std.Address]struct{}), + } + + // Add owner to auth list + a.authorized[a.Owner()] = struct{}{} + return a +} + +func NewAuthorizableWithAddress(addr std.Address) *Authorizable { + a := &Authorizable{ + ownable.NewOwnableWithAddress(addr), + make(map[std.Address]struct{}), + } + + // Add owner to auth list + a.authorized[addr] = struct{}{} + return a +} + +func (a *Authorizable) AddToAuthList(addr std.Address) error { + if err := a.CallerIsOwner(); err != nil { + return ErrNotInAuthList + } + + if _, exists := a.authorized[addr]; exists { + return ErrAlreadyInList + } + + a.authorized[addr] = struct{}{} + return nil +} + +func (a Authorizable) OnAuthList() error { + caller := std.PrevRealm().Addr() + + if _, exists := a.authorized[caller]; exists { + return nil + } + + return ErrNotInAuthList +} + +func (a Authorizable) AssertOnAuthList() { + caller := std.PrevRealm().Addr() + + if _, exists := a.authorized[caller]; !exists { + panic(ErrNotInAuthList) + } +} diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno new file mode 100644 index 00000000000..5377df8c767 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -0,0 +1,50 @@ +package authorizable + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" +) + +var ( + alice = testutils.TestAddress("alice") + bob = testutils.TestAddress("bob") + charlie = testutils.TestAddress("charlie") +) + +func TestNew(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug): should not be needed + + a := NewAuthorizable() + got := a.Owner() + + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } +} + +func TestNewWithAddress(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + + got := a.Owner() + + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } +} + +//func TestAddToAuthList(t *testing.T) { +// a := NewAuthorizable() +// +// if err := a.AddToAuthList(bob); err != nil { +// t.Fatalf("Expected AddToAuth to not error, but it did") +// } +// +// std.TestSetRealm(std.NewUserRealm(bob)) +// +// if err := a.AddToAuthList(charlie); err == nil { +// t.Fatalf("Expected AddToAuth to error, did not happen") +// } +//} diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno new file mode 100644 index 00000000000..5fddaba1c5e --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -0,0 +1,8 @@ +package authorizable + +import "errors" + +var ( + ErrNotInAuthList = errors.New("unauthorized; caller is not in authorized list") + ErrAlreadyInList = errors.New("address is already in authorized list") +) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod new file mode 100644 index 00000000000..0e8be79f130 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/ownable/exts/authorizable diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index 22c0c33acc6..cfd2d7b0011 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -1,8 +1,6 @@ package ownable -import ( - "std" -) +import "std" // Ownable is meant to be used as a top-level object to make your contract ownable OR // being embedded in a Gno object to manage per-object ownership. @@ -10,14 +8,16 @@ type Ownable struct { owner std.Address } -func New() *Ownable { +func NewOwnable() *Ownable { return &Ownable{ owner: std.PrevRealm().Addr(), } } -func NewWithAddress(addr std.Address) *Ownable { - return &Ownable{owner: addr} +func NewOwnableWithAddress(addr std.Address) *Ownable { + return &Ownable{ + owner: addr, + } } // TransferOwnership transfers ownership of the Ownable struct to a new address @@ -32,6 +32,7 @@ func (o *Ownable) TransferOwnership(newOwner std.Address) error { } o.owner = newOwner + return nil } @@ -48,6 +49,7 @@ func (o *Ownable) DropOwnership() error { return nil } +// Owner returns the owner address from Ownable func (o Ownable) Owner() std.Address { return o.owner } @@ -57,9 +59,11 @@ func (o Ownable) CallerIsOwner() error { if std.PrevRealm().Addr() == o.owner { return nil } + return ErrUnauthorized } +// AssertCallerIsOwner panics if the caller is not the owner func (o Ownable) AssertCallerIsOwner() { if std.PrevRealm().Addr() != o.owner { panic(ErrUnauthorized) diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno index 2c28f867f4a..2ffefdbc689 100644 --- a/examples/gno.land/p/demo/ownable/ownable_test.gno +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -3,38 +3,40 @@ package ownable import ( "std" "testing" + + "gno.land/p/demo/testutils" ) var ( - firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de") - secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa") + alice = testutils.TestAddress("alice") + bob = testutils.TestAddress("bob") ) func TestNew(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) - std.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug): should not be needed - o := New() + o := NewOwnable() got := o.Owner() - if firstCaller != got { - t.Fatalf("Expected %s, got: %s", firstCaller, got) + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) } } func TestNewWithAddress(t *testing.T) { - o := NewWithAddress(firstCaller) + o := NewOwnableWithAddress(alice) got := o.Owner() - if firstCaller != got { - t.Fatalf("Expected %s, got: %s", firstCaller, got) + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) } } func TestOwner(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) - o := New() - expected := firstCaller + o := NewOwnable() + expected := alice got := o.Owner() if expected != got { t.Fatalf("Expected %s, got: %s", expected, got) @@ -42,26 +44,26 @@ func TestOwner(t *testing.T) { } func TestTransferOwnership(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) - o := New() + o := NewOwnable() - err := o.TransferOwnership(secondCaller) + err := o.TransferOwnership(bob) if err != nil { t.Fatalf("TransferOwnership failed, %v", err) } got := o.Owner() - if secondCaller != got { - t.Fatalf("Expected: %s, got: %s", secondCaller, got) + if bob != got { + t.Fatalf("Expected: %s, got: %s", bob, got) } } func TestCallerIsOwner(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) - o := New() - unauthorizedCaller := secondCaller + o := NewOwnable() + unauthorizedCaller := bob std.TestSetRealm(std.NewUserRealm(unauthorizedCaller)) std.TestSetOrigCaller(unauthorizedCaller) // TODO(bug): should not be needed @@ -73,9 +75,9 @@ func TestCallerIsOwner(t *testing.T) { } func TestDropOwnership(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) - o := New() + o := NewOwnable() err := o.DropOwnership() if err != nil { @@ -91,15 +93,15 @@ func TestDropOwnership(t *testing.T) { // Errors func TestErrUnauthorized(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) - std.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug): should not be needed - o := New() + o := NewOwnable() - std.TestSetRealm(std.NewUserRealm(secondCaller)) - std.TestSetOrigCaller(secondCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) // TODO(bug): should not be needed - err := o.TransferOwnership(firstCaller) + err := o.TransferOwnership(alice) if err != ErrUnauthorized { t.Fatalf("Should've been ErrUnauthorized, was %v", err) } @@ -111,9 +113,9 @@ func TestErrUnauthorized(t *testing.T) { } func TestErrInvalidAddress(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) - o := New() + o := NewOwnable() err := o.TransferOwnership("") if err != ErrInvalidAddress { diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno new file mode 100644 index 00000000000..13a28330120 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -0,0 +1,8 @@ +package events + +import "errors" + +var ( + errEmptyName = errors.New("event name cannot be empty") + errInvalidStartTime = errors.New("event start time cannot be less than 0") +) diff --git a/examples/gno.land/r/gnoland/events/events.go b/examples/gno.land/r/gnoland/events/events.go new file mode 100644 index 00000000000..05fd7b42c51 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/events.go @@ -0,0 +1,91 @@ +package events + +import ( + "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" + "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + "time" + + "github.com/gnolang/gno/gnovm/stdlibs/std" + "gno.land/p/demo/ownable/exts/authorizable" +) + +type Event struct { + name string // name of event + description string // short description of event + link string // link to a corresponding web2 sign up page, ie eventbrite/luma + location string // location of the event + startTime time.Time // start time of the event +} + +var ( + a *authorizable.Authorizable + events *avl.Tree + idCounter seqid.ID +) + +func init() { + su := std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @moul + a = authorizable.NewWithAddress(su) + + a.AddToAuthList(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")) // @leohhhn + + events = avl.NewTree() +} + +func AddNewEvent(name, description, link, location string, startTime int64) { + a.AssertOnAuthList() + + if name == "" { + panic(errEmptyName) + } + + if startTime <= 0 { + panic(errInvalidStartTime) + } + + e := &Event{ + name: name, + description: description, + link: link, + location: location, + startTime: time.Unix(startTime, 0), // given in unix seconds + } + + _ = events.Set(genID(e.startTime), e) +} + +// RenderEventWidget shows up to amt of the latest events to a caller +func RenderEventWidget(amt int) string { + if events.Size() == 0 { + return "No events." + } + + var ( + i = 0 + output = "" + ) + + events.ReverseIterate("", "", func(key string, value interface{}) bool { + e := value.(*Event) + if e.startTime.After(time.Now()) { + output += ufmt.Sprintf("[%s](%s)", e.name, e.link) + i++ + } // only return upcoming events + + return i >= amt + }) + + return output +} + +func (e Event) RenderEventList() string { + +} + +// genID generates a unique id for the event +// By utilizing the AVL tree property which automatically sorts in lex order, +// we can automatically have events sorted by event start time +func genID(t time.Time) string { + return t.Format(time.RFC3339) + "-" + idCounter.Next().String() +} From 22ba7445e4a40d9b17db5c10ac126a60a46a4c29 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 09:40:49 +0200 Subject: [PATCH 03/60] save events --- examples/gno.land/r/gnoland/events/errors.gno | 1 + examples/gno.land/r/gnoland/events/events.go | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 13a28330120..7770fdef346 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -5,4 +5,5 @@ import "errors" var ( errEmptyName = errors.New("event name cannot be empty") errInvalidStartTime = errors.New("event start time cannot be less than 0") + errDescTooLong = errors.New("event description cannot be more than 80 chars") ) diff --git a/examples/gno.land/r/gnoland/events/events.go b/examples/gno.land/r/gnoland/events/events.go index 05fd7b42c51..f99b4f49890 100644 --- a/examples/gno.land/r/gnoland/events/events.go +++ b/examples/gno.land/r/gnoland/events/events.go @@ -16,6 +16,7 @@ type Event struct { link string // link to a corresponding web2 sign up page, ie eventbrite/luma location string // location of the event startTime time.Time // start time of the event + // add duration/endtime? } var ( @@ -33,7 +34,7 @@ func init() { events = avl.NewTree() } -func AddNewEvent(name, description, link, location string, startTime int64) { +func AddEvent(name, description, link, location string, startTime int64) { a.AssertOnAuthList() if name == "" { @@ -44,6 +45,10 @@ func AddNewEvent(name, description, link, location string, startTime int64) { panic(errInvalidStartTime) } + if len(description) > 80 { + panic(errDescTooLong) + } + e := &Event{ name: name, description: description, @@ -69,7 +74,7 @@ func RenderEventWidget(amt int) string { events.ReverseIterate("", "", func(key string, value interface{}) bool { e := value.(*Event) if e.startTime.After(time.Now()) { - output += ufmt.Sprintf("[%s](%s)", e.name, e.link) + output += ufmt.Sprintf("[%s](%s)\n", e.name, e.link) i++ } // only return upcoming events @@ -79,8 +84,24 @@ func RenderEventWidget(amt int) string { return output } -func (e Event) RenderEventList() string { +func RenderHome() string { + if events.Size() == 0 { + return "No upcoming or past events." + } + + output := "" + + events.ReverseIterate("", "", func(key string, value interface{}) bool { + e := value.(*Event) + if e.startTime.After(time.Now()) { + + i++ + } // only return upcoming events + + return i >= amt + }) + return output } // genID generates a unique id for the event From de5ec060eb8abbf0ceb00372fed335ad8d779bb4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 11:52:59 +0200 Subject: [PATCH 04/60] save --- .../gno.land/p/demo/memeland/memeland.gno | 2 +- .../exts/authorizable/authorizable.gno | 22 ++++-- .../exts/authorizable/authorizable_test.gno | 54 ++++++++++---- .../demo/ownable/exts/authorizable/errors.gno | 5 +- .../p/demo/ownable/exts/authorizable/gno.mod | 5 ++ .../gno.land/p/demo/pausable/pausable.gno | 2 +- .../gnoland/events/{events.go => events.gno} | 74 +++++++++++++++---- examples/gno.land/r/gnoland/events/gno.mod | 7 +- 8 files changed, 132 insertions(+), 39 deletions(-) rename examples/gno.land/r/gnoland/events/{events.go => events.gno} (61%) diff --git a/examples/gno.land/p/demo/memeland/memeland.gno b/examples/gno.land/p/demo/memeland/memeland.gno index 9c302ca365b..6a261fae0be 100644 --- a/examples/gno.land/p/demo/memeland/memeland.gno +++ b/examples/gno.land/p/demo/memeland/memeland.gno @@ -33,7 +33,7 @@ type Memeland struct { func NewMemeland() *Memeland { return &Memeland{ - Ownable: ownable.New(), + Ownable: ownable.NewOwnable(), Posts: make([]*Post, 0), } } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index 1b16be80c6d..e06918a48ae 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -12,8 +12,8 @@ import ( ) type Authorizable struct { - *ownable.Ownable - authorized map[std.Address]struct{} + *ownable.Ownable // owner in ownable is superuser + authorized map[std.Address]struct{} } func NewAuthorizable() *Authorizable { @@ -40,11 +40,11 @@ func NewAuthorizableWithAddress(addr std.Address) *Authorizable { func (a *Authorizable) AddToAuthList(addr std.Address) error { if err := a.CallerIsOwner(); err != nil { - return ErrNotInAuthList + return errNotSuperuser } if _, exists := a.authorized[addr]; exists { - return ErrAlreadyInList + return errAlreadyInList } a.authorized[addr] = struct{}{} @@ -53,18 +53,28 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { func (a Authorizable) OnAuthList() error { caller := std.PrevRealm().Addr() + if _, exists := a.authorized[caller]; exists { + return nil + } + return errNotInAuthList +} + +func (a Authorizable) OnAuthListByAddr(addr std.Address) error { + caller := std.PrevRealm().Addr() if _, exists := a.authorized[caller]; exists { return nil } - return ErrNotInAuthList + return errNotInAuthList } func (a Authorizable) AssertOnAuthList() { caller := std.PrevRealm().Addr() if _, exists := a.authorized[caller]; !exists { - panic(ErrNotInAuthList) + panic(errNotInAuthList) } } + +// todo add remove from auth list diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index 5377df8c767..1be204fea36 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -35,16 +35,44 @@ func TestNewWithAddress(t *testing.T) { } } -//func TestAddToAuthList(t *testing.T) { -// a := NewAuthorizable() -// -// if err := a.AddToAuthList(bob); err != nil { -// t.Fatalf("Expected AddToAuth to not error, but it did") -// } -// -// std.TestSetRealm(std.NewUserRealm(bob)) -// -// if err := a.AddToAuthList(charlie); err == nil { -// t.Fatalf("Expected AddToAuth to error, did not happen") -// } -//} +func TestOnList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.OnAuthList(); err != nil { + t.Fatalf("expected alice to be on the list") + } +} + +func TestNotOnList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + if err := a.OnAuthList(); err == nil { + t.Fatalf("expected bob to not be on the list") + } +} + +func TestAddToAuthList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.AddToAuthList(bob); err != nil { + println(err) + t.Fatalf("Expected AddToAuth to not error while adding bob, but it did") + } + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + if err := a.AddToAuthList(bob); err == nil { + t.Fatalf("Expected AddToAuth to error while bob called it, but it didn't") + } + + //if err := a.AddToAuthList(charlie); err == nil { + // t.Fatalf("Expected AddToAuth to error, did not happen") + //} +} diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno index 5fddaba1c5e..9ce8b178444 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -3,6 +3,7 @@ package authorizable import "errors" var ( - ErrNotInAuthList = errors.New("unauthorized; caller is not in authorized list") - ErrAlreadyInList = errors.New("address is already in authorized list") + errNotInAuthList = errors.New("unauthorized; caller is not in authorized list") + errNotSuperuser = errors.New("unauthorized; caller is not superuser") + errAlreadyInList = errors.New("address is already in authorized list") ) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod index 0e8be79f130..4f68f1766d3 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod @@ -1 +1,6 @@ module gno.land/p/demo/ownable/exts/authorizable + +require ( + gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest +) diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index eae3456ba61..f5ec0b65e2f 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -10,7 +10,7 @@ type Pausable struct { // New returns a new Pausable struct with non-paused state as default func New() *Pausable { return &Pausable{ - Ownable: ownable.New(), + Ownable: ownable.NewOwnable(), paused: false, } } diff --git a/examples/gno.land/r/gnoland/events/events.go b/examples/gno.land/r/gnoland/events/events.gno similarity index 61% rename from examples/gno.land/r/gnoland/events/events.go rename to examples/gno.land/r/gnoland/events/events.gno index f99b4f49890..48cf54b97ca 100644 --- a/examples/gno.land/r/gnoland/events/events.go +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -1,13 +1,14 @@ package events import ( - "github.com/gnolang/gno/examples/gno.land/p/demo/avl" - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" - "github.com/gnolang/gno/examples/gno.land/p/demo/ufmt" + "bytes" + "std" "time" - "github.com/gnolang/gno/gnovm/stdlibs/std" + "gno.land/p/demo/avl" "gno.land/p/demo/ownable/exts/authorizable" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" ) type Event struct { @@ -16,7 +17,7 @@ type Event struct { link string // link to a corresponding web2 sign up page, ie eventbrite/luma location string // location of the event startTime time.Time // start time of the event - // add duration/endtime? + // FIXME: add duration/endtime? } var ( @@ -26,10 +27,8 @@ var ( ) func init() { - su := std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") // @moul - a = authorizable.NewWithAddress(su) - - a.AddToAuthList(std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5")) // @leohhhn + su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @moul + a = authorizable.NewAuthorizableWithAddress(su) events = avl.NewTree() } @@ -61,11 +60,15 @@ func AddEvent(name, description, link, location string, startTime int64) { } // RenderEventWidget shows up to amt of the latest events to a caller -func RenderEventWidget(amt int) string { +func RenderEventWidget(amt uint) string { if events.Size() == 0 { return "No events." } + if amt > 5 { + panic("maximum number of events in widget is 5") + } + var ( i = 0 output = "" @@ -78,7 +81,7 @@ func RenderEventWidget(amt int) string { i++ } // only return upcoming events - return i >= amt + return i >= int(amt) }) return output @@ -91,22 +94,63 @@ func RenderHome() string { output := "" + output = "
\n\n" events.ReverseIterate("", "", func(key string, value interface{}) bool { e := value.(*Event) - if e.startTime.After(time.Now()) { - i++ - } // only return upcoming events + if e.startTime.After(time.Now()) { + output += e.RenderEvent() + } + if e.startTime.Before(time.Now()) { + output += e.RenderEvent() + } - return i >= amt + return false }) + output = "
\n\n" + return output } +func (e Event) RenderEvent() string { + var buf bytes.Buffer + + buf.WriteString("
") + + buf.WriteString(ufmt.Sprintf("#### %s\n\n", e.name)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.location)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.startTime.Format("02 Jan 2006"))) + + if e.startTime.Before(time.Now()) { + buf.WriteString("Finished\n\n") + } + + buf.WriteString("
") + + return buf.String() +} + +func Render(path string) string { + return RenderHome() +} + // genID generates a unique id for the event // By utilizing the AVL tree property which automatically sorts in lex order, // we can automatically have events sorted by event start time func genID(t time.Time) string { return t.Format(time.RFC3339) + "-" + idCounter.Next().String() } + +func GetOwner() std.Address { + return a.Owner() +} + +func AddModerator(mod std.Address) { + a.AssertCallerIsOwner() + + if err := a.AddToAuthList(mod); err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod index ec781c7cf10..67bf05c3461 100644 --- a/examples/gno.land/r/gnoland/events/gno.mod +++ b/examples/gno.land/r/gnoland/events/gno.mod @@ -1,3 +1,8 @@ module gno.land/r/gnoland/events -require gno.land/p/demo/ui v0.0.0-latest +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/ownable/exts/authorizable v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) From 8c3bbf1b224b0afc49225e870dc03dac1328d76f Mon Sep 17 00:00:00 2001 From: Leon Hudak <33522493+leohhhn@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:11:14 +0200 Subject: [PATCH 05/60] Update examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/ownable/exts/authorizable/authorizable.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index e06918a48ae..d3845fa9ae4 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -51,7 +51,7 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { return nil } -func (a Authorizable) OnAuthList() error { +func (a Authorizable) CallerOnAuthList() error { caller := std.PrevRealm().Addr() if _, exists := a.authorized[caller]; exists { return nil From 9a9e215feb07d596c0d5f85e6a91f0ab97728ef3 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 12:25:29 +0200 Subject: [PATCH 06/60] save --- .../ownable/exts/authorizable/authorizable.gno | 18 +++++++++--------- .../demo/ownable/exts/authorizable/errors.gno | 6 +++--- examples/gno.land/p/demo/ownable/ownable.gno | 4 ++-- examples/gno.land/r/gnoland/events/events.gno | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index d3845fa9ae4..8a62ddb72bd 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -18,7 +18,7 @@ type Authorizable struct { func NewAuthorizable() *Authorizable { a := &Authorizable{ - ownable.NewOwnable(), + ownable.New(), make(map[std.Address]struct{}), } @@ -29,7 +29,7 @@ func NewAuthorizable() *Authorizable { func NewAuthorizableWithAddress(addr std.Address) *Authorizable { a := &Authorizable{ - ownable.NewOwnableWithAddress(addr), + ownable.NewWithAddress(addr), make(map[std.Address]struct{}), } @@ -40,11 +40,11 @@ func NewAuthorizableWithAddress(addr std.Address) *Authorizable { func (a *Authorizable) AddToAuthList(addr std.Address) error { if err := a.CallerIsOwner(); err != nil { - return errNotSuperuser + return ErrNotSuperuser } if _, exists := a.authorized[addr]; exists { - return errAlreadyInList + return ErrAlreadyInList } a.authorized[addr] = struct{}{} @@ -53,11 +53,11 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { func (a Authorizable) CallerOnAuthList() error { caller := std.PrevRealm().Addr() - if _, exists := a.authorized[caller]; exists { - return nil + if _, exists := a.authorized[caller]; !exists { + return ErrNotInAuthList } - return errNotInAuthList + return nil } func (a Authorizable) OnAuthListByAddr(addr std.Address) error { @@ -66,14 +66,14 @@ func (a Authorizable) OnAuthListByAddr(addr std.Address) error { return nil } - return errNotInAuthList + return ErrNotInAuthList } func (a Authorizable) AssertOnAuthList() { caller := std.PrevRealm().Addr() if _, exists := a.authorized[caller]; !exists { - panic(errNotInAuthList) + panic(ErrNotInAuthList) } } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno index 9ce8b178444..6de7eda7a56 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -3,7 +3,7 @@ package authorizable import "errors" var ( - errNotInAuthList = errors.New("unauthorized; caller is not in authorized list") - errNotSuperuser = errors.New("unauthorized; caller is not superuser") - errAlreadyInList = errors.New("address is already in authorized list") + ErrNotInAuthList = errors.New("unauthorized; caller is not in authorized list") + ErrNotSuperuser = errors.New("unauthorized; caller is not superuser") + ErrAlreadyInList = errors.New("address is already in authorized list") ) diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index ab3b1e6ec81..a77b22461a9 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -10,13 +10,13 @@ type Ownable struct { owner std.Address } -func NewOwnable() *Ownable { +func New() *Ownable { return &Ownable{ owner: std.PrevRealm().Addr(), } } -func NewOwnableWithAddress(addr std.Address) *Ownable { +func NewWithAddress(addr std.Address) *Ownable { return &Ownable{ owner: addr, } diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 48cf54b97ca..6dee5fd4afa 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -27,7 +27,7 @@ var ( ) func init() { - su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @moul + su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn a = authorizable.NewAuthorizableWithAddress(su) events = avl.NewTree() From f6b809ab4d92248ed0f2ab043259a830c2ad531b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 13:38:38 +0200 Subject: [PATCH 07/60] save --- examples/gno.land/p/demo/ownable/errors.gno | 4 +- .../exts/authorizable/authorizable.gno | 46 +++++++++------ .../exts/authorizable/authorizable_test.gno | 57 ++++++++++++++++--- .../demo/ownable/exts/authorizable/errors.gno | 4 +- 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/errors.gno b/examples/gno.land/p/demo/ownable/errors.gno index ffbf6ab3f6f..89776a6cf12 100644 --- a/examples/gno.land/p/demo/ownable/errors.gno +++ b/examples/gno.land/p/demo/ownable/errors.gno @@ -3,6 +3,6 @@ package ownable import "errors" var ( - ErrUnauthorized = errors.New("unauthorized; caller is not owner") - ErrInvalidAddress = errors.New("new owner address is invalid") + ErrUnauthorized = errors.New("ownable: caller is not owner") + ErrInvalidAddress = errors.New("ownable: new owner address is invalid") ) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index 8a62ddb72bd..01928b8dadd 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -8,33 +8,35 @@ package authorizable import ( "std" + "gno.land/p/demo/avl" "gno.land/p/demo/ownable" + "gno.land/p/demo/ufmt" ) type Authorizable struct { - *ownable.Ownable // owner in ownable is superuser - authorized map[std.Address]struct{} + *ownable.Ownable // owner in ownable is superuser + authorized *avl.Tree // std.Addr > struct{}{} } func NewAuthorizable() *Authorizable { a := &Authorizable{ ownable.New(), - make(map[std.Address]struct{}), + avl.NewTree(), } // Add owner to auth list - a.authorized[a.Owner()] = struct{}{} + a.authorized.Set(a.Owner().String(), struct{}{}) return a } func NewAuthorizableWithAddress(addr std.Address) *Authorizable { a := &Authorizable{ ownable.NewWithAddress(addr), - make(map[std.Address]struct{}), + avl.NewTree(), } // Add owner to auth list - a.authorized[addr] = struct{}{} + a.authorized.Set(a.Owner().String(), struct{}{}) return a } @@ -43,38 +45,46 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { return ErrNotSuperuser } - if _, exists := a.authorized[addr]; exists { + if _, exists := a.authorized.Get(addr.String()); exists { return ErrAlreadyInList } - a.authorized[addr] = struct{}{} + a.authorized.Set(addr.String(), struct{}{}) + return nil } -func (a Authorizable) CallerOnAuthList() error { - caller := std.PrevRealm().Addr() - if _, exists := a.authorized[caller]; !exists { +func (a *Authorizable) DelFromAuthList(addr std.Address) error { + if err := a.CallerIsOwner(); err != nil { + return ErrNotSuperuser + } + + if !a.authorized.Has(addr.String()) { return ErrNotInAuthList } + if _, removed := a.authorized.Remove(addr.String()); !removed { + str := ufmt.Sprintf("authorizable: could not remove %s from auth list", addr.String()) + panic(str) + } + return nil } -func (a Authorizable) OnAuthListByAddr(addr std.Address) error { +func (a Authorizable) CallerOnAuthList() error { caller := std.PrevRealm().Addr() - if _, exists := a.authorized[caller]; exists { - return nil + + if !a.authorized.Has(caller.String()) { + return ErrNotInAuthList } - return ErrNotInAuthList + return nil } func (a Authorizable) AssertOnAuthList() { caller := std.PrevRealm().Addr() - if _, exists := a.authorized[caller]; !exists { + if !a.authorized.Has(caller.String()) { panic(ErrNotInAuthList) } } - -// todo add remove from auth list diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index 1be204fea36..dbf1d25a186 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -15,7 +15,7 @@ var ( func TestNew(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - std.TestSetOrigCaller(alice) // TODO(bug): should not be needed + std.TestSetOrigCaller(alice) // TODO(bug, issue #2371): should not be needed a := NewAuthorizable() got := a.Owner() @@ -40,7 +40,7 @@ func TestOnList(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) - if err := a.OnAuthList(); err != nil { + if err := a.CallerOnAuthList(); err == ErrNotInAuthList { t.Fatalf("expected alice to be on the list") } } @@ -50,7 +50,7 @@ func TestNotOnList(t *testing.T) { std.TestSetRealm(std.NewUserRealm(bob)) std.TestSetOrigCaller(bob) - if err := a.OnAuthList(); err == nil { + if err := a.CallerOnAuthList(); err == nil { t.Fatalf("expected bob to not be on the list") } } @@ -61,8 +61,7 @@ func TestAddToAuthList(t *testing.T) { std.TestSetOrigCaller(alice) if err := a.AddToAuthList(bob); err != nil { - println(err) - t.Fatalf("Expected AddToAuth to not error while adding bob, but it did") + t.Fatalf("Expected no error, got %v", err) } std.TestSetRealm(std.NewUserRealm(bob)) @@ -71,8 +70,50 @@ func TestAddToAuthList(t *testing.T) { if err := a.AddToAuthList(bob); err == nil { t.Fatalf("Expected AddToAuth to error while bob called it, but it didn't") } +} + +func TestDelFromList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.AddToAuthList(bob); err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + if err := a.AddToAuthList(charlie); err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + // Try an unauthorized deletion + if err := a.DelFromAuthList(alice); err == nil { + t.Fatalf("Expected DelFromAuth to error with %v", err) + } + + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.DelFromAuthList(charlie); err != nil { + t.Fatalf("Expected no error, got %v", err) + } +} + +func TestAssertOnList(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + a := NewAuthorizableWithAddress(alice) + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + defer func() { + if r := recover(); r == nil { + t.Fatalf("AssertCallerIsOnAuthList should have panicked") + } + }() - //if err := a.AddToAuthList(charlie); err == nil { - // t.Fatalf("Expected AddToAuth to error, did not happen") - //} + a.AssertOnAuthList() } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno index 6de7eda7a56..0d55bc05dcc 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -3,7 +3,7 @@ package authorizable import "errors" var ( - ErrNotInAuthList = errors.New("unauthorized; caller is not in authorized list") - ErrNotSuperuser = errors.New("unauthorized; caller is not superuser") + ErrNotInAuthList = errors.New("authorizable: caller is not in authorized list") + ErrNotSuperuser = errors.New("authorizable: caller is not superuser") ErrAlreadyInList = errors.New("address is already in authorized list") ) From e66aea80e1fb39c0f91566bf257b3aaaa96b8e4c Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 16:14:29 +0200 Subject: [PATCH 08/60] save --- .../gno.land/p/demo/memeland/memeland.gno | 2 +- .../gno.land/p/demo/pausable/pausable.gno | 2 +- examples/gno.land/r/gnoland/events/errors.gno | 1 - examples/gno.land/r/gnoland/events/events.gno | 43 +++++++++++-------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/examples/gno.land/p/demo/memeland/memeland.gno b/examples/gno.land/p/demo/memeland/memeland.gno index 6a261fae0be..9c302ca365b 100644 --- a/examples/gno.land/p/demo/memeland/memeland.gno +++ b/examples/gno.land/p/demo/memeland/memeland.gno @@ -33,7 +33,7 @@ type Memeland struct { func NewMemeland() *Memeland { return &Memeland{ - Ownable: ownable.NewOwnable(), + Ownable: ownable.New(), Posts: make([]*Post, 0), } } diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno index f5ec0b65e2f..eae3456ba61 100644 --- a/examples/gno.land/p/demo/pausable/pausable.gno +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -10,7 +10,7 @@ type Pausable struct { // New returns a new Pausable struct with non-paused state as default func New() *Pausable { return &Pausable{ - Ownable: ownable.NewOwnable(), + Ownable: ownable.New(), paused: false, } } diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 7770fdef346..13a28330120 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -5,5 +5,4 @@ import "errors" var ( errEmptyName = errors.New("event name cannot be empty") errInvalidStartTime = errors.New("event start time cannot be less than 0") - errDescTooLong = errors.New("event description cannot be more than 80 chars") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 6dee5fd4afa..8e5759d7728 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -26,6 +26,8 @@ var ( idCounter seqid.ID ) +const maxDescLength = 100 + func init() { su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn a = authorizable.NewAuthorizableWithAddress(su) @@ -44,8 +46,9 @@ func AddEvent(name, description, link, location string, startTime int64) { panic(errInvalidStartTime) } - if len(description) > 80 { - panic(errDescTooLong) + if len(description) > maxDescLength { + str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) + panic(str) } e := &Event{ @@ -87,53 +90,57 @@ func RenderEventWidget(amt uint) string { return output } -func RenderHome() string { +func renderHome() string { + output := "# Gno.land Events\n\n" + if events.Size() == 0 { - return "No upcoming or past events." + output += "No upcoming or past events." + return output } - output := "" + output += "
" - output = "
\n\n" events.ReverseIterate("", "", func(key string, value interface{}) bool { e := value.(*Event) if e.startTime.After(time.Now()) { - output += e.RenderEvent() + output += e.RenderListItem() } if e.startTime.Before(time.Now()) { - output += e.RenderEvent() + output += e.RenderListItem() } + output += ufmt.Sprintf("", key) + return false }) - output = "
\n\n" + output += "
" return output } -func (e Event) RenderEvent() string { +func (e Event) RenderListItem() string { var buf bytes.Buffer - buf.WriteString("
") - - buf.WriteString(ufmt.Sprintf("#### %s\n\n", e.name)) + buf.WriteString("
\n\n") + buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) - buf.WriteString(ufmt.Sprintf("%s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("%s\n\n", e.startTime.Format("02 Jan 2006"))) + buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) + buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006"))) + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) if e.startTime.Before(time.Now()) { - buf.WriteString("Finished\n\n") + buf.WriteString("*Event is over\n\n") } - buf.WriteString("
") + buf.WriteString("\n
") return buf.String() } func Render(path string) string { - return RenderHome() + return renderHome() } // genID generates a unique id for the event From a5c9191a71a50bb797097056467da1b2a12e3f48 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 18 Jun 2024 18:08:42 +0200 Subject: [PATCH 09/60] save --- examples/gno.land/r/gnoland/events/events.gno | 113 ++++++++++-------- examples/gno.land/r/gnoland/home/gno.mod | 1 + examples/gno.land/r/gnoland/home/home.gno | 8 +- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 8e5759d7728..8cb6230089f 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -2,12 +2,12 @@ package events import ( "bytes" + "sort" "std" + "strconv" "time" - "gno.land/p/demo/avl" "gno.land/p/demo/ownable/exts/authorizable" - "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) @@ -20,19 +20,23 @@ type Event struct { // FIXME: add duration/endtime? } +type eventsSlice []*Event + var ( - a *authorizable.Authorizable - events *avl.Tree - idCounter seqid.ID + a *authorizable.Authorizable + events eventsSlice ) -const maxDescLength = 100 +const ( + maxDescLength = 100 + maxWidgetSize = 5 +) func init() { su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn a = authorizable.NewAuthorizableWithAddress(su) - events = avl.NewTree() + events = make([]*Event, 10) } func AddEvent(name, description, link, location string, startTime int64) { @@ -59,33 +63,35 @@ func AddEvent(name, description, link, location string, startTime int64) { startTime: time.Unix(startTime, 0), // given in unix seconds } - _ = events.Set(genID(e.startTime), e) + events = append(events, e) + sort.Sort(events) } // RenderEventWidget shows up to amt of the latest events to a caller -func RenderEventWidget(amt uint) string { - if events.Size() == 0 { +func RenderEventWidget(amt int) string { + eventNum := len(events) + if eventNum == 0 { return "No events." } if amt > 5 { - panic("maximum number of events in widget is 5") + panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) } - var ( - i = 0 - output = "" - ) + if amt < 1 { + panic("amt needs to be more than 0") + } - events.ReverseIterate("", "", func(key string, value interface{}) bool { - e := value.(*Event) - if e.startTime.After(time.Now()) { - output += ufmt.Sprintf("[%s](%s)\n", e.name, e.link) - i++ - } // only return upcoming events + if amt > eventNum { + amt = eventNum - 1 + } - return i >= int(amt) - }) + output := "" + + for _, _ = range events[eventNum-amt:] { + //output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + output += "1" + } return output } @@ -93,29 +99,33 @@ func RenderEventWidget(amt uint) string { func renderHome() string { output := "# Gno.land Events\n\n" - if events.Size() == 0 { + if len(events) == 0 { output += "No upcoming or past events." return output } - output += "
" + output += "Below is a list of all upcoming and past Gno.land events.\n\n" + output += "---\n\n" - events.ReverseIterate("", "", func(key string, value interface{}) bool { - e := value.(*Event) - - if e.startTime.After(time.Now()) { - output += e.RenderListItem() - } - if e.startTime.Before(time.Now()) { - output += e.RenderListItem() - } + // Find position of latest past event + pos := sort.Search(len(events), func(i int) bool { + return events[i].startTime.After(time.Now()) + }) - output += ufmt.Sprintf("", key) + // Add events + for i, e := range events { + output += "
" + output += e.RenderListItem() + output += "
\n\n" - return false - }) + // Add separator if we are at the boundary between upcoming and past events + if i == pos-1 { + output += "---\n\n" + } + } - output += "
" + output += "---\n\n" + output += "##### *All times are local." return output } @@ -127,13 +137,14 @@ func (e Event) RenderListItem() string { buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006"))) - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) if e.startTime.Before(time.Now()) { buf.WriteString("*Event is over\n\n") } + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + buf.WriteString("\n") return buf.String() @@ -143,13 +154,6 @@ func Render(path string) string { return renderHome() } -// genID generates a unique id for the event -// By utilizing the AVL tree property which automatically sorts in lex order, -// we can automatically have events sorted by event start time -func genID(t time.Time) string { - return t.Format(time.RFC3339) + "-" + idCounter.Next().String() -} - func GetOwner() std.Address { return a.Owner() } @@ -161,3 +165,18 @@ func AddModerator(mod std.Address) { panic(err) } } + +// Len returns the length of the slice +func (m eventsSlice) Len() int { + return len(m) +} + +// Less compares the startTime fields of two elements +func (m eventsSlice) Less(i, j int) bool { + return m[i].startTime.Before(m[j].startTime) +} + +// Swap swaps two elements in the slice +func (m eventsSlice) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} diff --git a/examples/gno.land/r/gnoland/home/gno.mod b/examples/gno.land/r/gnoland/home/gno.mod index 2864958930c..2791e4497ad 100644 --- a/examples/gno.land/r/gnoland/home/gno.mod +++ b/examples/gno.land/r/gnoland/home/gno.mod @@ -4,4 +4,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/ui v0.0.0-latest gno.land/r/gnoland/blog v0.0.0-latest + gno.land/r/gnoland/events v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 07ce461020b..78a261e7b72 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -6,6 +6,7 @@ import ( "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" blog "gno.land/r/gnoland/blog" + "gno.land/r/gnoland/events" ) // XXX: p/demo/ui API is crappy, we need to make it more idiomatic @@ -58,7 +59,7 @@ func Render(_ string) string { func lastBlogposts(limit int) ui.Element { posts := blog.RenderLastPostsWidget(limit) return ui.Element{ - ui.H3("Latest Blogposts"), + ui.H3("[Latest Blogposts](/r/gnoland/blog)"), ui.Text(posts), } } @@ -73,9 +74,8 @@ func lastContributions(limit int) ui.Element { func upcomingEvents(limit int) ui.Element { return ui.Element{ - ui.H3("Upcoming Events"), - // TODO: replace with r/gnoland/events - ui.Text("[View upcoming events](/events)"), + ui.H3("[Upcoming Events](/r/gnoland/events)"), + ui.Text(events.RenderEventWidget(5)), } } From 55e4e90345c36ab9eae49225d5743fe85ddaa050 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 19 Jun 2024 17:54:05 +0200 Subject: [PATCH 10/60] save --- examples/gno.land/r/gnoland/events/events.gno | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 8cb6230089f..78f6b88abdb 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -2,6 +2,7 @@ package events import ( "bytes" + "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" "sort" "std" "strconv" @@ -12,6 +13,7 @@ import ( ) type Event struct { + id seqid.ID name string // name of event description string // short description of event link string // link to a corresponding web2 sign up page, ie eventbrite/luma @@ -23,8 +25,9 @@ type Event struct { type eventsSlice []*Event var ( - a *authorizable.Authorizable - events eventsSlice + a *authorizable.Authorizable + events eventsSlice + idCounter seqid.ID ) const ( @@ -36,7 +39,7 @@ func init() { su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn a = authorizable.NewAuthorizableWithAddress(su) - events = make([]*Event, 10) + events = make(eventsSlice, 0) } func AddEvent(name, description, link, location string, startTime int64) { @@ -56,6 +59,7 @@ func AddEvent(name, description, link, location string, startTime int64) { } e := &Event{ + id: idCounter.Next(), name: name, description: description, link: link, @@ -67,6 +71,15 @@ func AddEvent(name, description, link, location string, startTime int64) { sort.Sort(events) } +func DelEvent(id seqid.ID) { + a.AssertOnAuthList() + + sort.Find() + +} + +// Rendering + // RenderEventWidget shows up to amt of the latest events to a caller func RenderEventWidget(amt int) string { eventNum := len(events) From 8a20f6f2a0ea1131d03fab01c338825e621a33db Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 01:23:12 +0200 Subject: [PATCH 11/60] add upcoming & past rendering --- examples/gno.land/r/gnoland/events/events.gno | 49 ++++++++++--------- examples/gno.land/r/gnoland/home/home.gno | 2 +- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 78f6b88abdb..f27c36c6791 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -2,13 +2,13 @@ package events import ( "bytes" - "github.com/gnolang/gno/examples/gno.land/p/demo/seqid" "sort" "std" "strconv" "time" "gno.land/p/demo/ownable/exts/authorizable" + "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) @@ -26,7 +26,7 @@ type eventsSlice []*Event var ( a *authorizable.Authorizable - events eventsSlice + events eventsSlice // sorted oldest > newest idCounter seqid.ID ) @@ -74,8 +74,6 @@ func AddEvent(name, description, link, location string, startTime int64) { func DelEvent(id seqid.ID) { a.AssertOnAuthList() - sort.Find() - } // Rendering @@ -101,9 +99,8 @@ func RenderEventWidget(amt int) string { output := "" - for _, _ = range events[eventNum-amt:] { - //output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) - output += "1" + for _, event := range events[eventNum-amt:] { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) } return output @@ -122,24 +119,30 @@ func renderHome() string { // Find position of latest past event pos := sort.Search(len(events), func(i int) bool { - return events[i].startTime.After(time.Now()) + return events[i].startTime.Before(time.Now()) }) - // Add events - for i, e := range events { - output += "
" - output += e.RenderListItem() - output += "
\n\n" + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" - // Add separator if we are at the boundary between upcoming and past events - if i == pos-1 { - output += "---\n\n" - } + for _, e := range events[:pos] { + output += e.RenderListItem() } + output += "
\n\n" + output += "---\n\n" - output += "##### *All times are local." + // Add past events + output += "## Past events\n\n" + output += "
" + + for _, e := range events[pos:] { + output += e.RenderListItem() + } + + output += "
\n\n" return output } @@ -152,13 +155,13 @@ func (e Event) RenderListItem() string { buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) - if e.startTime.Before(time.Now()) { - buf.WriteString("*Event is over\n\n") - } + //if e.startTime.Before(time.Now()) { + // buf.WriteString("*Event is over\n\n") + //} buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) - buf.WriteString("\n") + buf.WriteString("") return buf.String() } @@ -186,7 +189,7 @@ func (m eventsSlice) Len() int { // Less compares the startTime fields of two elements func (m eventsSlice) Less(i, j int) bool { - return m[i].startTime.Before(m[j].startTime) + return m[i].startTime.After(m[j].startTime) } // Swap swaps two elements in the slice diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 78a261e7b72..2c0f8d9defa 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -6,7 +6,7 @@ import ( "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" blog "gno.land/r/gnoland/blog" - "gno.land/r/gnoland/events" + events "gno.land/r/gnoland/events" ) // XXX: p/demo/ui API is crappy, we need to make it more idiomatic From c47fc123bee494793eeed6b85bc4b57ccea9f79d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 01:53:03 +0200 Subject: [PATCH 12/60] del func --- examples/gno.land/r/gnoland/events/events.gno | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index f27c36c6791..229bb2d9e63 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -19,7 +19,7 @@ type Event struct { link string // link to a corresponding web2 sign up page, ie eventbrite/luma location string // location of the event startTime time.Time // start time of the event - // FIXME: add duration/endtime? + // add duration/endtime? } type eventsSlice []*Event @@ -71,9 +71,19 @@ func AddEvent(name, description, link, location string, startTime int64) { sort.Sort(events) } -func DelEvent(id seqid.ID) { +func DelEvent(id seqid.ID) string { a.AssertOnAuthList() + for _, event := range events { + if event.id == id { + // todo see if there is a bug in the vm + // --= Error =-- + // Data: interface conversion: gnolang.RefValue is not gnolang.Object: missing method DecRefCount + return "found THE ID: " + strconv.Itoa(int(event.id)) + } + } + + return "" } // Rendering @@ -126,7 +136,7 @@ func renderHome() string { output += "## Upcoming events\n\n" output += "
" - for _, e := range events[:pos] { + for _, e := range events { output += e.RenderListItem() } @@ -160,13 +170,14 @@ func (e Event) RenderListItem() string { //} buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + buf.WriteString(ufmt.Sprintf(" \n\n", e.id)) buf.WriteString("
") return buf.String() } -func Render(path string) string { +func Render(_ string) string { return renderHome() } From 26104ec3bb238b4c6a79c16b449fde52f7b990f3 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 02:02:48 +0200 Subject: [PATCH 13/60] rm --- examples/gno.land/r/gnoland/events/events.gno | 209 ------------------ 1 file changed, 209 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 229bb2d9e63..e69de29bb2d 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -1,209 +0,0 @@ -package events - -import ( - "bytes" - "sort" - "std" - "strconv" - "time" - - "gno.land/p/demo/ownable/exts/authorizable" - "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" -) - -type Event struct { - id seqid.ID - name string // name of event - description string // short description of event - link string // link to a corresponding web2 sign up page, ie eventbrite/luma - location string // location of the event - startTime time.Time // start time of the event - // add duration/endtime? -} - -type eventsSlice []*Event - -var ( - a *authorizable.Authorizable - events eventsSlice // sorted oldest > newest - idCounter seqid.ID -) - -const ( - maxDescLength = 100 - maxWidgetSize = 5 -) - -func init() { - su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn - a = authorizable.NewAuthorizableWithAddress(su) - - events = make(eventsSlice, 0) -} - -func AddEvent(name, description, link, location string, startTime int64) { - a.AssertOnAuthList() - - if name == "" { - panic(errEmptyName) - } - - if startTime <= 0 { - panic(errInvalidStartTime) - } - - if len(description) > maxDescLength { - str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) - panic(str) - } - - e := &Event{ - id: idCounter.Next(), - name: name, - description: description, - link: link, - location: location, - startTime: time.Unix(startTime, 0), // given in unix seconds - } - - events = append(events, e) - sort.Sort(events) -} - -func DelEvent(id seqid.ID) string { - a.AssertOnAuthList() - - for _, event := range events { - if event.id == id { - // todo see if there is a bug in the vm - // --= Error =-- - // Data: interface conversion: gnolang.RefValue is not gnolang.Object: missing method DecRefCount - return "found THE ID: " + strconv.Itoa(int(event.id)) - } - } - - return "" -} - -// Rendering - -// RenderEventWidget shows up to amt of the latest events to a caller -func RenderEventWidget(amt int) string { - eventNum := len(events) - if eventNum == 0 { - return "No events." - } - - if amt > 5 { - panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) - } - - if amt < 1 { - panic("amt needs to be more than 0") - } - - if amt > eventNum { - amt = eventNum - 1 - } - - output := "" - - for _, event := range events[eventNum-amt:] { - output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) - } - - return output -} - -func renderHome() string { - output := "# Gno.land Events\n\n" - - if len(events) == 0 { - output += "No upcoming or past events." - return output - } - - output += "Below is a list of all upcoming and past Gno.land events.\n\n" - output += "---\n\n" - - // Find position of latest past event - pos := sort.Search(len(events), func(i int) bool { - return events[i].startTime.Before(time.Now()) - }) - - // Add upcoming events - output += "## Upcoming events\n\n" - output += "
" - - for _, e := range events { - output += e.RenderListItem() - } - - output += "
\n\n" - - output += "---\n\n" - - // Add past events - output += "## Past events\n\n" - output += "
" - - for _, e := range events[pos:] { - output += e.RenderListItem() - } - - output += "
\n\n" - return output -} - -func (e Event) RenderListItem() string { - var buf bytes.Buffer - - buf.WriteString("
\n\n") - buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) - buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) - buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) - - //if e.startTime.Before(time.Now()) { - // buf.WriteString("*Event is over\n\n") - //} - - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) - buf.WriteString(ufmt.Sprintf(" \n\n", e.id)) - - buf.WriteString("
") - - return buf.String() -} - -func Render(_ string) string { - return renderHome() -} - -func GetOwner() std.Address { - return a.Owner() -} - -func AddModerator(mod std.Address) { - a.AssertCallerIsOwner() - - if err := a.AddToAuthList(mod); err != nil { - panic(err) - } -} - -// Len returns the length of the slice -func (m eventsSlice) Len() int { - return len(m) -} - -// Less compares the startTime fields of two elements -func (m eventsSlice) Less(i, j int) bool { - return m[i].startTime.After(m[j].startTime) -} - -// Swap swaps two elements in the slice -func (m eventsSlice) Swap(i, j int) { - m[i], m[j] = m[j], m[i] -} From f7513b3196221d89b7ceb0d00152369ffbb52259 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 02:02:57 +0200 Subject: [PATCH 14/60] add --- examples/gno.land/r/gnoland/events/events.gno | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index e69de29bb2d..229bb2d9e63 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -0,0 +1,209 @@ +package events + +import ( + "bytes" + "sort" + "std" + "strconv" + "time" + + "gno.land/p/demo/ownable/exts/authorizable" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) + +type Event struct { + id seqid.ID + name string // name of event + description string // short description of event + link string // link to a corresponding web2 sign up page, ie eventbrite/luma + location string // location of the event + startTime time.Time // start time of the event + // add duration/endtime? +} + +type eventsSlice []*Event + +var ( + a *authorizable.Authorizable + events eventsSlice // sorted oldest > newest + idCounter seqid.ID +) + +const ( + maxDescLength = 100 + maxWidgetSize = 5 +) + +func init() { + su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn + a = authorizable.NewAuthorizableWithAddress(su) + + events = make(eventsSlice, 0) +} + +func AddEvent(name, description, link, location string, startTime int64) { + a.AssertOnAuthList() + + if name == "" { + panic(errEmptyName) + } + + if startTime <= 0 { + panic(errInvalidStartTime) + } + + if len(description) > maxDescLength { + str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) + panic(str) + } + + e := &Event{ + id: idCounter.Next(), + name: name, + description: description, + link: link, + location: location, + startTime: time.Unix(startTime, 0), // given in unix seconds + } + + events = append(events, e) + sort.Sort(events) +} + +func DelEvent(id seqid.ID) string { + a.AssertOnAuthList() + + for _, event := range events { + if event.id == id { + // todo see if there is a bug in the vm + // --= Error =-- + // Data: interface conversion: gnolang.RefValue is not gnolang.Object: missing method DecRefCount + return "found THE ID: " + strconv.Itoa(int(event.id)) + } + } + + return "" +} + +// Rendering + +// RenderEventWidget shows up to amt of the latest events to a caller +func RenderEventWidget(amt int) string { + eventNum := len(events) + if eventNum == 0 { + return "No events." + } + + if amt > 5 { + panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) + } + + if amt < 1 { + panic("amt needs to be more than 0") + } + + if amt > eventNum { + amt = eventNum - 1 + } + + output := "" + + for _, event := range events[eventNum-amt:] { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + } + + return output +} + +func renderHome() string { + output := "# Gno.land Events\n\n" + + if len(events) == 0 { + output += "No upcoming or past events." + return output + } + + output += "Below is a list of all upcoming and past Gno.land events.\n\n" + output += "---\n\n" + + // Find position of latest past event + pos := sort.Search(len(events), func(i int) bool { + return events[i].startTime.Before(time.Now()) + }) + + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" + + for _, e := range events { + output += e.RenderListItem() + } + + output += "
\n\n" + + output += "---\n\n" + + // Add past events + output += "## Past events\n\n" + output += "
" + + for _, e := range events[pos:] { + output += e.RenderListItem() + } + + output += "
\n\n" + return output +} + +func (e Event) RenderListItem() string { + var buf bytes.Buffer + + buf.WriteString("
\n\n") + buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) + buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) + buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) + + //if e.startTime.Before(time.Now()) { + // buf.WriteString("*Event is over\n\n") + //} + + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + buf.WriteString(ufmt.Sprintf(" \n\n", e.id)) + + buf.WriteString("
") + + return buf.String() +} + +func Render(_ string) string { + return renderHome() +} + +func GetOwner() std.Address { + return a.Owner() +} + +func AddModerator(mod std.Address) { + a.AssertCallerIsOwner() + + if err := a.AddToAuthList(mod); err != nil { + panic(err) + } +} + +// Len returns the length of the slice +func (m eventsSlice) Len() int { + return len(m) +} + +// Less compares the startTime fields of two elements +func (m eventsSlice) Less(i, j int) bool { + return m[i].startTime.After(m[j].startTime) +} + +// Swap swaps two elements in the slice +func (m eventsSlice) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} From 9d9140acc2c4fa0133a3e6fff7ee25a5b48aec75 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 02:04:56 +0200 Subject: [PATCH 15/60] del --- examples/gno.land/r/gnoland/events/events.gno | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 229bb2d9e63..ae30fe268de 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -71,19 +71,15 @@ func AddEvent(name, description, link, location string, startTime int64) { sort.Sort(events) } -func DelEvent(id seqid.ID) string { +func DelEvent(id seqid.ID) { a.AssertOnAuthList() - for _, event := range events { + for i, event := range events { if event.id == id { - // todo see if there is a bug in the vm - // --= Error =-- - // Data: interface conversion: gnolang.RefValue is not gnolang.Object: missing method DecRefCount - return "found THE ID: " + strconv.Itoa(int(event.id)) + events = append(events[:i], events[i+1:]...) + return } } - - return "" } // Rendering From dd2f4aaeb9d31ec1479c1dc6e4baf6997cf7bef1 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 11:32:53 +0200 Subject: [PATCH 16/60] edit func --- examples/gno.land/r/gnoland/events/events.gno | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index ae30fe268de..f497a76b85d 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -82,6 +82,33 @@ func DelEvent(id seqid.ID) { } } +func EditEvent(id seqid.ID, name, description, link, location string, startTime int64) { + a.AssertOnAuthList() + + var prevStartTime time.Time + newStartTime := time.Unix(startTime, 0) + for i, event := range events { + if event.id == id { + prevStartTime = event.startTime + events[i] = &Event{ + id: id, + name: name, + description: description, + link: link, + location: location, + startTime: newStartTime, + } + + return + } + } + + // If sort order was disrupted, sort again + if newStartTime != prevStartTime { + sort.Sort(events) + } +} + // Rendering // RenderEventWidget shows up to amt of the latest events to a caller From fb63100ddc93bde79d79caaddbf4623979966a00 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 27 Jun 2024 13:37:27 +0200 Subject: [PATCH 17/60] renderHome edit --- examples/gno.land/r/gnoland/events/events.gno | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index f497a76b85d..32055cb7b49 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -109,6 +109,7 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime } } + // Rendering // RenderEventWidget shows up to amt of the latest events to a caller @@ -148,6 +149,7 @@ func renderHome() string { } output += "Below is a list of all upcoming and past Gno.land events.\n\n" + output += "---\n\n" // Find position of latest past event @@ -159,12 +161,15 @@ func renderHome() string { output += "## Upcoming events\n\n" output += "
" - for _, e := range events { - output += e.RenderListItem() + if len(events[:pos]) > 0 { + for _, e := range events[:pos] { + output += e.RenderListItem() + } + } else { + output += "No upcoming events.\n\n" } output += "
\n\n" - output += "---\n\n" // Add past events @@ -186,12 +191,7 @@ func (e Event) RenderListItem() string { buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) - - //if e.startTime.Before(time.Now()) { - // buf.WriteString("*Event is over\n\n") - //} - + buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04")) buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) buf.WriteString(ufmt.Sprintf(" \n\n", e.id)) From 4993c620255cd4b2f03785f9e38afe33f71e6c80 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sun, 30 Jun 2024 21:39:19 +0200 Subject: [PATCH 18/60] admin page --- examples/gno.land/r/gnoland/events/events.gno | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 32055cb7b49..60969f14936 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -109,7 +109,6 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime } } - // Rendering // RenderEventWidget shows up to amt of the latest events to a caller @@ -128,11 +127,12 @@ func RenderEventWidget(amt int) string { } if amt > eventNum { - amt = eventNum - 1 + amt = eventNum } output := "" + // todo check for ordering for _, event := range events[eventNum-amt:] { output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) } @@ -140,7 +140,7 @@ func RenderEventWidget(amt int) string { return output } -func renderHome() string { +func renderHome(admin bool) string { output := "# Gno.land Events\n\n" if len(events) == 0 { @@ -163,7 +163,7 @@ func renderHome() string { if len(events[:pos]) > 0 { for _, e := range events[:pos] { - output += e.RenderListItem() + output += e.Render(admin) } } else { output += "No upcoming events.\n\n" @@ -177,31 +177,38 @@ func renderHome() string { output += "
" for _, e := range events[pos:] { - output += e.RenderListItem() + output += e.Render(admin) } output += "
\n\n" return output } -func (e Event) RenderListItem() string { +func (e Event) Render(admin bool) string { var buf bytes.Buffer buf.WriteString("
\n\n") buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04")) - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) - buf.WriteString(ufmt.Sprintf(" \n\n", e.id)) + buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) + + if admin { + buf.WriteString(ufmt.Sprintf("Event ID: %s\n\n", e.id)) + } + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) buf.WriteString("
") return buf.String() } -func Render(_ string) string { - return renderHome() +func Render(path string) string { + if path == "admin" { + return renderHome(true) + } + + return renderHome(false) } func GetOwner() std.Address { From aa42d70ee106850dc01bd3051c0494a083c4544b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sun, 30 Jun 2024 21:45:07 +0200 Subject: [PATCH 19/60] past evts --- examples/gno.land/r/gnoland/events/events.gno | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 60969f14936..616ab216d6a 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -161,6 +161,7 @@ func renderHome(admin bool) string { output += "## Upcoming events\n\n" output += "
" + // If there are any upcoming events if len(events[:pos]) > 0 { for _, e := range events[:pos] { output += e.Render(admin) @@ -176,8 +177,12 @@ func renderHome(admin bool) string { output += "## Past events\n\n" output += "
" - for _, e := range events[pos:] { - output += e.Render(admin) + if len(events[pos:]) > 0 { + for _, e := range events[pos:] { + output += e.Render(admin) + } + } else { + output += "No past events.\n\n" } output += "
\n\n" From ffd686a4614ff5596814a7e8becab33f23367707 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sun, 30 Jun 2024 21:46:11 +0200 Subject: [PATCH 20/60] comment --- examples/gno.land/r/gnoland/events/events.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 616ab216d6a..d076205f25f 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -16,7 +16,7 @@ type Event struct { id seqid.ID name string // name of event description string // short description of event - link string // link to a corresponding web2 sign up page, ie eventbrite/luma + link string // link to a corresponding web2 page, ie eventbrite/luma or conference page location string // location of the event startTime time.Time // start time of the event // add duration/endtime? From bacab7b87cb202d42bba4d55f55df45d627ff39b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 12 Jul 2024 22:11:26 -0500 Subject: [PATCH 21/60] play around with gnoweb links --- examples/gno.land/r/gnoland/events/events.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index d076205f25f..41ac03009ac 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -200,6 +200,7 @@ func (e Event) Render(admin bool) string { if admin { buf.WriteString(ufmt.Sprintf("Event ID: %s\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) } buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) From edf77e153541d291ee36bc1ef5795288b01495f8 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 18 Jul 2024 06:02:05 +0200 Subject: [PATCH 22/60] add endtime, delete admin button --- examples/gno.land/r/gnoland/events/errors.gno | 4 +-- examples/gno.land/r/gnoland/events/events.gno | 30 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 13a28330120..2d202d1ad44 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -3,6 +3,6 @@ package events import "errors" var ( - errEmptyName = errors.New("event name cannot be empty") - errInvalidStartTime = errors.New("event start time cannot be less than 0") + ErrEmptyName = errors.New("event name cannot be empty") + ErrInvalidStartTime = errors.New("event start time cannot be less than 0") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 41ac03009ac..55bff3bd834 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -19,7 +19,7 @@ type Event struct { link string // link to a corresponding web2 page, ie eventbrite/luma or conference page location string // location of the event startTime time.Time // start time of the event - // add duration/endtime? + endTime time.Time // end time of the event } type eventsSlice []*Event @@ -42,15 +42,15 @@ func init() { events = make(eventsSlice, 0) } -func AddEvent(name, description, link, location string, startTime int64) { +func AddEvent(name, description, link, location string, startTime, endTime int64) { a.AssertOnAuthList() if name == "" { - panic(errEmptyName) + panic(ErrEmptyName) } if startTime <= 0 { - panic(errInvalidStartTime) + panic(ErrInvalidStartTime) } if len(description) > maxDescLength { @@ -65,6 +65,7 @@ func AddEvent(name, description, link, location string, startTime int64) { link: link, location: location, startTime: time.Unix(startTime, 0), // given in unix seconds + endTime: time.Unix(endTime, 0), // given in unix seconds } events = append(events, e) @@ -82,11 +83,13 @@ func DelEvent(id seqid.ID) { } } -func EditEvent(id seqid.ID, name, description, link, location string, startTime int64) { +func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { a.AssertOnAuthList() var prevStartTime time.Time newStartTime := time.Unix(startTime, 0) + newEndTime := time.Unix(endTime, 0) + for i, event := range events { if event.id == id { prevStartTime = event.startTime @@ -97,6 +100,7 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime link: link, location: location, startTime: newStartTime, + endTime: newEndTime, } return @@ -141,14 +145,14 @@ func RenderEventWidget(amt int) string { } func renderHome(admin bool) string { - output := "# Gno.land Events\n\n" + output := "# gno.land events\n\n" if len(events) == 0 { output += "No upcoming or past events." return output } - output += "Below is a list of all upcoming and past Gno.land events.\n\n" + output += "Below is a list of all upcoming and past gno.land events.\n\n" output += "---\n\n" @@ -186,6 +190,10 @@ func renderHome(admin bool) string { } output += "
\n\n" + + output += "---\n\n" + output += "#### *All times are local." + return output } @@ -196,14 +204,16 @@ func (e Event) Render(admin bool) string { buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Time & Date:** %s\n\n", e.startTime.Format("02 Jan 2006, 15:04"))) + buf.WriteString(ufmt.Sprintf("**Start time:** %s\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"))) + buf.WriteString(ufmt.Sprintf("**End time:** %s\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"))) if admin { - buf.WriteString(ufmt.Sprintf("Event ID: %s\n\n", e.id)) buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DelEvent&id=%s)\n\n", e.id)) + } else { + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) } - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) buf.WriteString("") return buf.String() From b9cdfbb9bf4eac91d5b36021da8dfa0ced20d51e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 18 Jul 2024 07:16:46 +0200 Subject: [PATCH 23/60] add tests --- examples/gno.land/r/gnoland/events/errors.gno | 1 + examples/gno.land/r/gnoland/events/events.gno | 8 +- .../gno.land/r/gnoland/events/events_test.gno | 81 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 examples/gno.land/r/gnoland/events/events_test.gno diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 2d202d1ad44..3869ccb32fb 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -5,4 +5,5 @@ import "errors" var ( ErrEmptyName = errors.New("event name cannot be empty") ErrInvalidStartTime = errors.New("event start time cannot be less than 0") + ErrInvalidEndTime = errors.New("event end time cannot be less than 0 or before start time") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 55bff3bd834..ae95d7ba07b 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -53,6 +53,10 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 panic(ErrInvalidStartTime) } + if endTime <= 0 || endTime <= startTime { + panic(ErrInvalidEndTime) + } + if len(description) > maxDescLength { str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) panic(str) @@ -210,7 +214,9 @@ func (e Event) Render(admin bool) string { if admin { buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DelEvent&id=%s)\n\n", e.id)) - } else { + } + + if e.link != "" { buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) } diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno new file mode 100644 index 00000000000..29f579702ff --- /dev/null +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -0,0 +1,81 @@ +package events + +import ( + "std" + "strings" + "testing" + "time" + + "gno.land/p/demo/uassert" +) + +var ( + su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn + suRealm = std.NewUserRealm(su) +) + +func TestAddEvent(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + now := time.Now() + + e1Start := now.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Unix(), e1End.Unix()) + + got := renderHome(false) + + if !strings.Contains(got, "Event 1") { + t.Fatalf("Expected to find Event 1 in render") + } + + e2Start := now.Add(-time.Hour * 24 * 5) + e2End := e2Start.Add(time.Hour * 4) + + AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Unix(), e2End.Unix()) + + got = renderHome(false) + + upcomingPos := strings.Index(got, "## Upcoming events") + pastPos := strings.Index(got, "## Past events") + + e1Pos := strings.Index(got, "Event 1") + e2Pos := strings.Index(got, "Event 2") + + // expected index ordering: upcoming < e1 < past < e2 + if e1Pos < upcomingPos || e1Pos > pastPos { + t.Fatalf("Expected to find Event 1 in Upcoming events") + } + + if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { + t.Fatalf("Expected to find Event 2 on a different pos") + } +} + +func TestAddEventErrors(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + uassert.PanicsWithMessage(t, ErrEmptyName.Error(), func() { + AddEvent("", "sample desc", "gno.land", "gnome land", 123, 456) + }) + + uassert.PanicsWithMessage(t, ErrInvalidStartTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 0, 456) + }) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 0) + }) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 1) + }) + + tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` + // figure out a way to check these panics in a better way + uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { + AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) + }) +} From f3d9d8b4f5b649845741a233db93c1ea7a53e2ff Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 18 Jul 2024 07:18:11 +0200 Subject: [PATCH 24/60] update error --- examples/gno.land/r/gnoland/events/errors.gno | 2 +- examples/gno.land/r/gnoland/events/events.gno | 2 +- examples/gno.land/r/gnoland/events/events_test.gno | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 3869ccb32fb..3e8d499d0c6 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -5,5 +5,5 @@ import "errors" var ( ErrEmptyName = errors.New("event name cannot be empty") ErrInvalidStartTime = errors.New("event start time cannot be less than 0") - ErrInvalidEndTime = errors.New("event end time cannot be less than 0 or before start time") + ErrInvalidEndTime = errors.New("event end time cannot be before start time") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index ae95d7ba07b..8774b318b57 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -53,7 +53,7 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 panic(ErrInvalidStartTime) } - if endTime <= 0 || endTime <= startTime { + if endTime <= startTime { panic(ErrInvalidEndTime) } diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 29f579702ff..339cc92d910 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -51,6 +51,7 @@ func TestAddEvent(t *testing.T) { if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { t.Fatalf("Expected to find Event 2 on a different pos") } + } func TestAddEventErrors(t *testing.T) { From 8288c1f694720ec79ea3752bf7fffa57dae1649f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 18 Jul 2024 08:03:30 +0200 Subject: [PATCH 25/60] tests --- examples/gno.land/r/gnoland/events/events.gno | 2 +- examples/gno.land/r/gnoland/events/events_test.gno | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 8774b318b57..bc5ca51c82d 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -26,7 +26,7 @@ type eventsSlice []*Event var ( a *authorizable.Authorizable - events eventsSlice // sorted oldest > newest + events eventsSlice // sorted oldest > newest by startTime idCounter seqid.ID ) diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 339cc92d910..e02baae5243 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -52,6 +52,9 @@ func TestAddEvent(t *testing.T) { t.Fatalf("Expected to find Event 2 on a different pos") } + if events[0].startTime.Unix() > events[1].startTime.Unix() { + t.Fatalf("expected ordering to be different") + } } func TestAddEventErrors(t *testing.T) { From a7b4178dfb137e64931ee922cf191139f4fec993 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 18 Jul 2024 13:29:12 +0200 Subject: [PATCH 26/60] mod tidy --- examples/gno.land/r/gnoland/events/gno.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod index 67bf05c3461..5a4c6ac56f3 100644 --- a/examples/gno.land/r/gnoland/events/gno.mod +++ b/examples/gno.land/r/gnoland/events/gno.mod @@ -1,8 +1,8 @@ module gno.land/r/gnoland/events require ( - gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/ownable/exts/authorizable v0.0.0-latest gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest ) From 9eeeec192986b6ae5ca0370354ffcbefb2a7a37b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 07:14:42 +0200 Subject: [PATCH 27/60] add tests --- examples/gno.land/r/gnoland/events/errors.gno | 1 + examples/gno.land/r/gnoland/events/events.gno | 93 +++++++++++++------ .../gno.land/r/gnoland/events/events_test.gno | 29 +++++- 3 files changed, 94 insertions(+), 29 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 3e8d499d0c6..1179882ba9b 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -6,4 +6,5 @@ var ( ErrEmptyName = errors.New("event name cannot be empty") ErrInvalidStartTime = errors.New("event start time cannot be less than 0") ErrInvalidEndTime = errors.New("event end time cannot be before start time") + ErrNoSuchID = errors.New("event with specified ID does not exist") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index bc5ca51c82d..a2403809425 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -26,13 +26,16 @@ type eventsSlice []*Event var ( a *authorizable.Authorizable - events eventsSlice // sorted oldest > newest by startTime + events eventsSlice // oldest event has the largest index idCounter seqid.ID ) const ( maxDescLength = 100 maxWidgetSize = 5 + AdditionEvent = "EventAdded" + DeletionEvent = "EventDeleted" + EditingEvent = "EventEdited" ) func init() { @@ -42,7 +45,7 @@ func init() { events = make(eventsSlice, 0) } -func AddEvent(name, description, link, location string, startTime, endTime int64) { +func AddEvent(name, description, link, location string, startTime, endTime int64) seqid.ID { a.AssertOnAuthList() if name == "" { @@ -62,8 +65,9 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 panic(str) } + id := idCounter.Next() e := &Event{ - id: idCounter.Next(), + id: id, name: name, description: description, link: link, @@ -74,17 +78,29 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 events = append(events, e) sort.Sort(events) + + std.Emit(AdditionEvent, + "id", + e.id.String(), + ) + + return id } func DelEvent(id seqid.ID) { a.AssertOnAuthList() - for i, event := range events { - if event.id == id { - events = append(events[:i], events[i+1:]...) - return - } + e, idx, err := GetEventByID(id) + if err != nil { + panic(err) } + + events = append(events[:idx], events[idx+1:]...) + + std.Emit(DeletionEvent, + "id", + e.id.String(), + ) } func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { @@ -94,23 +110,28 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, newStartTime := time.Unix(startTime, 0) newEndTime := time.Unix(endTime, 0) - for i, event := range events { - if event.id == id { - prevStartTime = event.startTime - events[i] = &Event{ - id: id, - name: name, - description: description, - link: link, - location: location, - startTime: newStartTime, - endTime: newEndTime, - } - - return - } + e, _, err := GetEventByID(id) + if err != nil { + panic(err) } + prevStartTime = e.startTime + + e = &Event{ + id: id, + name: name, + description: description, + link: link, + location: location, + startTime: newStartTime, + endTime: newEndTime, + } + + std.Emit(EditingEvent, + "id", + e.id.String(), + ) + // If sort order was disrupted, sort again if newStartTime != prevStartTime { sort.Sort(events) @@ -140,9 +161,10 @@ func RenderEventWidget(amt int) string { output := "" - // todo check for ordering for _, event := range events[eventNum-amt:] { - output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + if event.startTime.After(time.Now()) { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + } } return output @@ -157,10 +179,11 @@ func renderHome(admin bool) string { } output += "Below is a list of all upcoming and past gno.land events.\n\n" + output += ufmt.Sprintf(time.Now().Format(time.RFC850)) output += "---\n\n" - // Find position of latest past event + // Find position of la1test past event pos := sort.Search(len(events), func(i int) bool { return events[i].startTime.Before(time.Now()) }) @@ -229,7 +252,12 @@ func Render(path string) string { if path == "admin" { return renderHome(true) } - + //out := "" + // + //for _, e := range events { + // out += e.Render(false) + //} + //return out return renderHome(false) } @@ -245,12 +273,23 @@ func AddModerator(mod std.Address) { } } +func GetEventByID(id seqid.ID) (*Event, int, error) { + for i, event := range events { + if event.id == id { + return event, i, nil + } + } + + return nil, -1, ErrNoSuchID +} + // Len returns the length of the slice func (m eventsSlice) Len() int { return len(m) } // Less compares the startTime fields of two elements +// In this case, events will be sorted by largest startTime first (upcoming > past) func (m eventsSlice) Less(i, j int) bool { return m[i].startTime.After(m[j].startTime) } diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index e02baae5243..ea3c061fa4f 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -52,7 +52,8 @@ func TestAddEvent(t *testing.T) { t.Fatalf("Expected to find Event 2 on a different pos") } - if events[0].startTime.Unix() > events[1].startTime.Unix() { + // larger index => smaller startTime (future => past) + if events[0].startTime.Unix() < events[1].startTime.Unix() { t.Fatalf("expected ordering to be different") } } @@ -78,8 +79,32 @@ func TestAddEventErrors(t *testing.T) { }) tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` - // figure out a way to check these panics in a better way + // figure out a way to check panics with dynamic params in a better way uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) }) } + +func TestDelEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) + got := renderHome(false) + + if !strings.Contains(got, "ToDelete") { + t.Fatalf("Expected to find ToDelete event in render") + } + + DelEvent(id) + got = renderHome(false) + + if strings.Contains(got, "ToDelete") { + t.Fatalf("Did not expect to find ToDelete event in render") + } +} + +func TestEditEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) + +} From f48150662fb50c49d5c485cfb71359e4b2f26041 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 07:20:03 +0200 Subject: [PATCH 28/60] add godoc --- examples/gno.land/r/gnoland/events/events.gno | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index a2403809425..239a870e31b 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -45,6 +45,8 @@ func init() { events = make(eventsSlice, 0) } +// AddEvent adds a new event +// Start time & end time need to be specified in unix seconds func AddEvent(name, description, link, location string, startTime, endTime int64) seqid.ID { a.AssertOnAuthList() @@ -87,6 +89,7 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 return id } +// DelEvent deletes an event with a given ID func DelEvent(id seqid.ID) { a.AssertOnAuthList() @@ -103,28 +106,39 @@ func DelEvent(id seqid.ID) { ) } +// EditEvent edits an event with a given ID +// It only updates values corresponding to non-empty/non-nil arguments sent with the call func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { a.AssertOnAuthList() - var prevStartTime time.Time - newStartTime := time.Unix(startTime, 0) - newEndTime := time.Unix(endTime, 0) - e, _, err := GetEventByID(id) if err != nil { panic(err) } - prevStartTime = e.startTime + // Set only valid values + if name != "" { + e.name = name + } - e = &Event{ - id: id, - name: name, - description: description, - link: link, - location: location, - startTime: newStartTime, - endTime: newEndTime, + if description != "" { + e.description = description + } + + if link != "" { + e.link = link + } + + if location != "" { + e.location = location + } + + if startTime > 0 { + e.startTime = time.Unix(startTime, 0) + } + + if endTime > 0 { + e.endTime = time.Unix(endTime, 0) } std.Emit(EditingEvent, @@ -133,7 +147,7 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, ) // If sort order was disrupted, sort again - if newStartTime != prevStartTime { + if startTime > 0 { sort.Sort(events) } } From 3dae9f40ccb1058836eb19ec87fd17a43b3b4a86 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 07:28:20 +0200 Subject: [PATCH 29/60] edit tests --- examples/gno.land/r/gnoland/events/events.gno | 4 ++- .../gno.land/r/gnoland/events/events_test.gno | 34 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 239a870e31b..ef87718b7a7 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -137,8 +137,10 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, e.startTime = time.Unix(startTime, 0) } - if endTime > 0 { + if endTime > 0 && endTime > startTime { e.endTime = time.Unix(endTime, 0) + } else { + panic(ErrInvalidEndTime) } std.Emit(EditingEvent, diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index ea3c061fa4f..af956b96630 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -105,6 +105,38 @@ func TestDelEvent(t *testing.T) { func TestEditEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) + id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) + + newName := "New Name" + newDesc := "Normal description" + newST := int64(1234) + newET := int64(4567) + + EditEvent(id, newName, newDesc, "", "", newST, newET) + edited, _, _ := GetEventByID(id) + + if edited.name != newName { + t.Fatalf("Expected %s, got %s", newName, edited.name) + } + + if edited.description != newDesc { + t.Fatalf("Expected %s, got %s", newDesc, edited.description) + } + + if edited.startTime != time.Unix(newST, 0) { + t.Fatalf("Expected %d, got %d", newST, edited.startTime.Unix()) + } + + if edited.endTime != time.Unix(newET, 0) { + t.Fatalf("Expected %d, got %d", newET, edited.endTime.Unix()) + } +} +func TestInvalidEdit(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + EditEvent(id, "", "", "", "", 0, -5) + }) } From ed0ada1bd1e9c72a0afbc51be7236b90d868c287 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 07:36:29 +0200 Subject: [PATCH 30/60] update authorizable test --- .../ownable/exts/authorizable/authorizable_test.gno | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index dbf1d25a186..6bf322ce66a 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -5,6 +5,7 @@ import ( "testing" "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" ) var ( @@ -109,11 +110,8 @@ func TestAssertOnList(t *testing.T) { std.TestSetRealm(std.NewUserRealm(bob)) std.TestSetOrigCaller(bob) - defer func() { - if r := recover(); r == nil { - t.Fatalf("AssertCallerIsOnAuthList should have panicked") - } - }() + uassert.PanicsWithMessage(t, ErrNotInAuthList.Error(), func() { + a.AssertOnAuthList() + }) - a.AssertOnAuthList() } From 097dc6a8c7ba8206a43f962c89f9f15b810a7379 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 11:25:12 +0200 Subject: [PATCH 31/60] mod tidy and lint --- .../p/demo/ownable/exts/authorizable/gno.mod | 3 +++ .../gno.land/p/demo/ownable/ownable_test.gno | 17 +++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod index 4f68f1766d3..f36823f3f71 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod @@ -1,6 +1,9 @@ module gno.land/p/demo/ownable/exts/authorizable require ( + gno.land/p/demo/avl v0.0.0-latest gno.land/p/demo/ownable v0.0.0-latest gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno index 4a973f13d3c..a9d97154f45 100644 --- a/examples/gno.land/p/demo/ownable/ownable_test.gno +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -5,6 +5,7 @@ import ( "testing" "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" ) var ( @@ -16,7 +17,7 @@ func TestNew(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) // TODO(bug): should not be needed - o := NewOwnable() + o := New() got := o.Owner() if alice != got { t.Fatalf("Expected %s, got: %s", alice, got) @@ -24,7 +25,7 @@ func TestNew(t *testing.T) { } func TestNewWithAddress(t *testing.T) { - o := NewOwnableWithAddress(alice) + o := NewWithAddress(alice) got := o.Owner() if alice != got { @@ -35,7 +36,7 @@ func TestNewWithAddress(t *testing.T) { func TestOwner(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - o := NewOwnable() + o := New() expected := alice got := o.Owner() uassert.Equal(t, expected, got) @@ -44,7 +45,7 @@ func TestOwner(t *testing.T) { func TestTransferOwnership(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - o := NewOwnable() + o := New() err := o.TransferOwnership(bob) if err != nil { @@ -60,7 +61,7 @@ func TestTransferOwnership(t *testing.T) { func TestCallerIsOwner(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - o := NewOwnable() + o := New() unauthorizedCaller := bob std.TestSetRealm(std.NewUserRealm(unauthorizedCaller)) @@ -73,7 +74,7 @@ func TestCallerIsOwner(t *testing.T) { func TestDropOwnership(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - o := NewOwnable() + o := New() err := o.DropOwnership() uassert.NoError(t, err, "DropOwnership failed") @@ -88,7 +89,7 @@ func TestErrUnauthorized(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) // TODO(bug): should not be needed - o := NewOwnable() + o := New() std.TestSetRealm(std.NewUserRealm(bob)) std.TestSetOrigCaller(bob) // TODO(bug): should not be needed @@ -105,7 +106,7 @@ func TestErrUnauthorized(t *testing.T) { func TestErrInvalidAddress(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) - o := NewOwnable() + o := New() err := o.TransferOwnership("") uassert.ErrorContains(t, err, ErrInvalidAddress.Error()) From ef89f74271832c916d709c3ef49f21bd26f43d26 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 19 Jul 2024 11:44:16 +0200 Subject: [PATCH 32/60] update home test --- examples/gno.land/r/gnoland/home/home_filetest.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 2b0a802718f..17477401fbb 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -68,15 +68,15 @@ func main() { //
//
// -// ### Latest Blogposts +// ### [Latest Blogposts](/r/gnoland/blog) // // No posts. //
//
// -// ### Upcoming Events +// ### [Upcoming Events](/r/gnoland/events) // -// [View upcoming events](/events) +// No events. //
//
// From af4a043d00519ec8de8ab68126dd5e97f1d92e57 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:29:18 +0200 Subject: [PATCH 33/60] a>auth --- examples/gno.land/r/gnoland/events/events.gno | 28 +++++++++---------- .../gno.land/r/gnoland/events/events_test.gno | 4 +-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index ef87718b7a7..ccc1bbe3599 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -16,7 +16,7 @@ type Event struct { id seqid.ID name string // name of event description string // short description of event - link string // link to a corresponding web2 page, ie eventbrite/luma or conference page + link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page location string // location of the event startTime time.Time // start time of the event endTime time.Time // end time of the event @@ -25,7 +25,7 @@ type Event struct { type eventsSlice []*Event var ( - a *authorizable.Authorizable + auth *authorizable.Authorizable events eventsSlice // oldest event has the largest index idCounter seqid.ID ) @@ -40,15 +40,15 @@ const ( func init() { su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn - a = authorizable.NewAuthorizableWithAddress(su) + auth = authorizable.NewAuthorizableWithAddress(su) events = make(eventsSlice, 0) } -// AddEvent adds a new event +// AddEvent adds auth new event // Start time & end time need to be specified in unix seconds func AddEvent(name, description, link, location string, startTime, endTime int64) seqid.ID { - a.AssertOnAuthList() + auth.AssertOnAuthList() if name == "" { panic(ErrEmptyName) @@ -89,9 +89,9 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 return id } -// DelEvent deletes an event with a given ID +// DelEvent deletes an event with auth given ID func DelEvent(id seqid.ID) { - a.AssertOnAuthList() + auth.AssertOnAuthList() e, idx, err := GetEventByID(id) if err != nil { @@ -106,10 +106,10 @@ func DelEvent(id seqid.ID) { ) } -// EditEvent edits an event with a given ID +// EditEvent edits an event with auth given ID // It only updates values corresponding to non-empty/non-nil arguments sent with the call func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { - a.AssertOnAuthList() + auth.AssertOnAuthList() e, _, err := GetEventByID(id) if err != nil { @@ -156,7 +156,7 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, // Rendering -// RenderEventWidget shows up to amt of the latest events to a caller +// RenderEventWidget shows up to amt of the latest events to auth caller func RenderEventWidget(amt int) string { eventNum := len(events) if eventNum == 0 { @@ -194,7 +194,7 @@ func renderHome(admin bool) string { return output } - output += "Below is a list of all upcoming and past gno.land events.\n\n" + output += "Below is auth list of all upcoming and past gno.land events.\n\n" output += ufmt.Sprintf(time.Now().Format(time.RFC850)) output += "---\n\n" @@ -278,13 +278,13 @@ func Render(path string) string { } func GetOwner() std.Address { - return a.Owner() + return auth.Owner() } func AddModerator(mod std.Address) { - a.AssertCallerIsOwner() + auth.AssertCallerIsOwner() - if err := a.AddToAuthList(mod); err != nil { + if err := auth.AddToAuthList(mod); err != nil { panic(err) } } diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index af956b96630..e0fe3079d53 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -49,7 +49,7 @@ func TestAddEvent(t *testing.T) { } if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { - t.Fatalf("Expected to find Event 2 on a different pos") + t.Fatalf("Expected to find Event 2 on auth different pos") } // larger index => smaller startTime (future => past) @@ -79,7 +79,7 @@ func TestAddEventErrors(t *testing.T) { }) tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` - // figure out a way to check panics with dynamic params in a better way + // figure out auth way to check panics with dynamic params in auth better way uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) }) From 79cca297987ee29895a8a22fa161a6d6106535c6 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:32:16 +0200 Subject: [PATCH 34/60] update event var names --- examples/gno.land/r/gnoland/events/events.gno | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index ccc1bbe3599..fa95033690c 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -33,9 +33,9 @@ var ( const ( maxDescLength = 100 maxWidgetSize = 5 - AdditionEvent = "EventAdded" - DeletionEvent = "EventDeleted" - EditingEvent = "EventEdited" + EventAdded = "EventAdded" + EventDeleted = "EventDeleted" + EventEdited = "EventEdited" ) func init() { @@ -81,7 +81,7 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 events = append(events, e) sort.Sort(events) - std.Emit(AdditionEvent, + std.Emit(EventAdded, "id", e.id.String(), ) @@ -100,7 +100,7 @@ func DelEvent(id seqid.ID) { events = append(events[:idx], events[idx+1:]...) - std.Emit(DeletionEvent, + std.Emit(EventDeleted, "id", e.id.String(), ) @@ -143,7 +143,7 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, panic(ErrInvalidEndTime) } - std.Emit(EditingEvent, + std.Emit(EventEdited, "id", e.id.String(), ) From 11d500d21881408b2eea474f9e8ab4b35a9cd0de Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:35:35 +0200 Subject: [PATCH 35/60] trimspace --- examples/gno.land/r/gnoland/events/events.gno | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index fa95033690c..8594f4c1bec 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -5,6 +5,7 @@ import ( "sort" "std" "strconv" + "strings" "time" "gno.land/p/demo/ownable/exts/authorizable" @@ -50,7 +51,7 @@ func init() { func AddEvent(name, description, link, location string, startTime, endTime int64) seqid.ID { auth.AssertOnAuthList() - if name == "" { + if strings.TrimSpace(name) == "" { panic(ErrEmptyName) } @@ -117,19 +118,19 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, } // Set only valid values - if name != "" { + if strings.TrimSpace(name) != "" { e.name = name } - if description != "" { + if strings.TrimSpace(description) != "" { e.description = description } - if link != "" { + if strings.TrimSpace(link) != "" { e.link = link } - if location != "" { + if strings.TrimSpace(location) != "" { e.location = location } From 486d21d9767f943892f0c474c8f4111e0d2d1eda Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:36:40 +0200 Subject: [PATCH 36/60] flip if case --- examples/gno.land/r/gnoland/events/events.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 8594f4c1bec..ccf584e27d8 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -138,12 +138,12 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, e.startTime = time.Unix(startTime, 0) } - if endTime > 0 && endTime > startTime { - e.endTime = time.Unix(endTime, 0) - } else { + if endTime < 0 || endTime < startTime { panic(ErrInvalidEndTime) } + e.endTime = time.Unix(endTime, 0) + std.Emit(EventEdited, "id", e.id.String(), From d89e79739401b804763c3ed49abd5850f714b40d Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:37:13 +0200 Subject: [PATCH 37/60] update widget arg --- examples/gno.land/r/gnoland/events/events.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index ccf584e27d8..964c5364178 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -158,13 +158,13 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, // Rendering // RenderEventWidget shows up to amt of the latest events to auth caller -func RenderEventWidget(amt int) string { +func RenderEventWidget(eventsToRender int) string { eventNum := len(events) if eventNum == 0 { return "No events." } - if amt > 5 { + if eventsToRender > 5 { panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) } From b96bc60ecb98081585f65effdbd69a45f34350e6 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:38:01 +0200 Subject: [PATCH 38/60] update naming in widget --- examples/gno.land/r/gnoland/events/events.gno | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 964c5364178..d7aa9b9e947 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -159,8 +159,8 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, // RenderEventWidget shows up to amt of the latest events to auth caller func RenderEventWidget(eventsToRender int) string { - eventNum := len(events) - if eventNum == 0 { + numOfEvents := len(events) + if numOfEvents == 0 { return "No events." } @@ -168,12 +168,12 @@ func RenderEventWidget(eventsToRender int) string { panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) } - if amt < 1 { - panic("amt needs to be more than 0") + if eventsToRender < 1 { + panic("you need to request at least 1 event to render") } - if amt > eventNum { - amt = eventNum + if eventsToRender > numOfEvents { + eventsToRender = numOfEvents } output := "" From acb9b1491522896e6588b1b806afc2f7c5ab4b3b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 15:38:23 +0200 Subject: [PATCH 39/60] fix widget --- examples/gno.land/r/gnoland/events/events.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index d7aa9b9e947..139592a7983 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -178,7 +178,7 @@ func RenderEventWidget(eventsToRender int) string { output := "" - for _, event := range events[eventNum-amt:] { + for _, event := range events[numOfEvents-eventsToRender:] { if event.startTime.After(time.Now()) { output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) } From aad4123061dd920e0e1dcaa2fb733f66abdc0073 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 6 Aug 2024 18:28:23 +0200 Subject: [PATCH 40/60] typo --- examples/gno.land/r/gnoland/events/events.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 139592a7983..92d7f90cb88 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -200,7 +200,7 @@ func renderHome(admin bool) string { output += "---\n\n" - // Find position of la1test past event + // Find position of the latest past event pos := sort.Search(len(events), func(i int) bool { return events[i].startTime.Before(time.Now()) }) From 555f1233e04db4aa4d68daf6bbf4d8a750cbc515 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 01:11:21 +0200 Subject: [PATCH 41/60] rename index --- examples/gno.land/r/gnoland/events/events.gno | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 92d7f90cb88..e228bed30af 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -201,7 +201,7 @@ func renderHome(admin bool) string { output += "---\n\n" // Find position of the latest past event - pos := sort.Search(len(events), func(i int) bool { + pastEventsIdx := sort.Search(len(events), func(i int) bool { return events[i].startTime.Before(time.Now()) }) @@ -210,8 +210,8 @@ func renderHome(admin bool) string { output += "
" // If there are any upcoming events - if len(events[:pos]) > 0 { - for _, e := range events[:pos] { + if len(events[:pastEventsIdx]) > 0 { + for _, e := range events[:pastEventsIdx] { output += e.Render(admin) } } else { @@ -225,8 +225,8 @@ func renderHome(admin bool) string { output += "## Past events\n\n" output += "
" - if len(events[pos:]) > 0 { - for _, e := range events[pos:] { + if len(events[pastEventsIdx:]) > 0 { + for _, e := range events[pastEventsIdx:] { output += e.Render(admin) } } else { From 3245bd05410d20d140a9f2f69a765dba945f75a5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 01:12:50 +0200 Subject: [PATCH 42/60] update authorizable err --- examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno index 0d55bc05dcc..4ba5983bccb 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -5,5 +5,5 @@ import "errors" var ( ErrNotInAuthList = errors.New("authorizable: caller is not in authorized list") ErrNotSuperuser = errors.New("authorizable: caller is not superuser") - ErrAlreadyInList = errors.New("address is already in authorized list") + ErrAlreadyInList = errors.New("authorizable: address is already in authorized list") ) From 6bfd617c279a0966e8a05b74450f622a712fcac3 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 01:15:01 +0200 Subject: [PATCH 43/60] update test names --- .../demo/ownable/exts/authorizable/authorizable_test.gno | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index 6bf322ce66a..fe39424dfc4 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -14,7 +14,7 @@ var ( charlie = testutils.TestAddress("charlie") ) -func TestNew(t *testing.T) { +func NewAuthorizable(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) // TODO(bug, issue #2371): should not be needed @@ -26,7 +26,7 @@ func TestNew(t *testing.T) { } } -func TestNewWithAddress(t *testing.T) { +func NewAuthorizableWithAddress(t *testing.T) { a := NewAuthorizableWithAddress(alice) got := a.Owner() @@ -36,7 +36,7 @@ func TestNewWithAddress(t *testing.T) { } } -func TestOnList(t *testing.T) { +func CallerOnAuthList(t *testing.T) { a := NewAuthorizableWithAddress(alice) std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) @@ -46,7 +46,7 @@ func TestOnList(t *testing.T) { } } -func TestNotOnList(t *testing.T) { +func TestNotCallerOnAuthList(t *testing.T) { a := NewAuthorizableWithAddress(alice) std.TestSetRealm(std.NewUserRealm(bob)) std.TestSetOrigCaller(bob) From 82ae082153061637dacaade1a949a75259f33920 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 01:38:34 +0200 Subject: [PATCH 44/60] update test names v2 --- .../demo/ownable/exts/authorizable/authorizable.gno | 2 +- .../ownable/exts/authorizable/authorizable_test.gno | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno index 01928b8dadd..f9f0ea15dd9 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -54,7 +54,7 @@ func (a *Authorizable) AddToAuthList(addr std.Address) error { return nil } -func (a *Authorizable) DelFromAuthList(addr std.Address) error { +func (a *Authorizable) DeleteFromAuthList(addr std.Address) error { if err := a.CallerIsOwner(); err != nil { return ErrNotSuperuser } diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno index fe39424dfc4..10a5e411bdb 100644 --- a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -14,7 +14,7 @@ var ( charlie = testutils.TestAddress("charlie") ) -func NewAuthorizable(t *testing.T) { +func TestNewAuthorizable(t *testing.T) { std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) // TODO(bug, issue #2371): should not be needed @@ -26,7 +26,7 @@ func NewAuthorizable(t *testing.T) { } } -func NewAuthorizableWithAddress(t *testing.T) { +func TestNewAuthorizableWithAddress(t *testing.T) { a := NewAuthorizableWithAddress(alice) got := a.Owner() @@ -36,7 +36,7 @@ func NewAuthorizableWithAddress(t *testing.T) { } } -func CallerOnAuthList(t *testing.T) { +func TestCallerOnAuthList(t *testing.T) { a := NewAuthorizableWithAddress(alice) std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) @@ -73,7 +73,7 @@ func TestAddToAuthList(t *testing.T) { } } -func TestDelFromList(t *testing.T) { +func TestDeleteFromList(t *testing.T) { a := NewAuthorizableWithAddress(alice) std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) @@ -90,14 +90,14 @@ func TestDelFromList(t *testing.T) { std.TestSetOrigCaller(bob) // Try an unauthorized deletion - if err := a.DelFromAuthList(alice); err == nil { + if err := a.DeleteFromAuthList(alice); err == nil { t.Fatalf("Expected DelFromAuth to error with %v", err) } std.TestSetRealm(std.NewUserRealm(alice)) std.TestSetOrigCaller(alice) - if err := a.DelFromAuthList(charlie); err != nil { + if err := a.DeleteFromAuthList(charlie); err != nil { t.Fatalf("Expected no error, got %v", err) } } @@ -113,5 +113,4 @@ func TestAssertOnList(t *testing.T) { uassert.PanicsWithMessage(t, ErrNotInAuthList.Error(), func() { a.AssertOnAuthList() }) - } From 0447cc40cc82bddf88ee495ce12c0af1654221c1 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 01:45:39 +0200 Subject: [PATCH 45/60] split events.gno --- examples/gno.land/r/gnoland/events/admin.gno | 21 +++ examples/gno.land/r/gnoland/events/events.gno | 139 ------------------ .../gno.land/r/gnoland/events/rendering.gno | 122 +++++++++++++++ 3 files changed, 143 insertions(+), 139 deletions(-) create mode 100644 examples/gno.land/r/gnoland/events/admin.gno create mode 100644 examples/gno.land/r/gnoland/events/rendering.gno diff --git a/examples/gno.land/r/gnoland/events/admin.gno b/examples/gno.land/r/gnoland/events/admin.gno new file mode 100644 index 00000000000..550ed6bf3bf --- /dev/null +++ b/examples/gno.land/r/gnoland/events/admin.gno @@ -0,0 +1,21 @@ +package events + +import ( + "std" + + "gno.land/p/demo/ownable/exts/authorizable" +) + +var auth *authorizable.Authorizable + +func GetOwner() std.Address { + return auth.Owner() +} + +func AddModerator(mod std.Address) { + auth.AssertCallerIsOwner() + + if err := auth.AddToAuthList(mod); err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index e228bed30af..69ae7d497b3 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -1,10 +1,8 @@ package events import ( - "bytes" "sort" "std" - "strconv" "strings" "time" @@ -26,14 +24,12 @@ type Event struct { type eventsSlice []*Event var ( - auth *authorizable.Authorizable events eventsSlice // oldest event has the largest index idCounter seqid.ID ) const ( maxDescLength = 100 - maxWidgetSize = 5 EventAdded = "EventAdded" EventDeleted = "EventDeleted" EventEdited = "EventEdited" @@ -155,141 +151,6 @@ func EditEvent(id seqid.ID, name, description, link, location string, startTime, } } -// Rendering - -// RenderEventWidget shows up to amt of the latest events to auth caller -func RenderEventWidget(eventsToRender int) string { - numOfEvents := len(events) - if numOfEvents == 0 { - return "No events." - } - - if eventsToRender > 5 { - panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) - } - - if eventsToRender < 1 { - panic("you need to request at least 1 event to render") - } - - if eventsToRender > numOfEvents { - eventsToRender = numOfEvents - } - - output := "" - - for _, event := range events[numOfEvents-eventsToRender:] { - if event.startTime.After(time.Now()) { - output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) - } - } - - return output -} - -func renderHome(admin bool) string { - output := "# gno.land events\n\n" - - if len(events) == 0 { - output += "No upcoming or past events." - return output - } - - output += "Below is auth list of all upcoming and past gno.land events.\n\n" - output += ufmt.Sprintf(time.Now().Format(time.RFC850)) - - output += "---\n\n" - - // Find position of the latest past event - pastEventsIdx := sort.Search(len(events), func(i int) bool { - return events[i].startTime.Before(time.Now()) - }) - - // Add upcoming events - output += "## Upcoming events\n\n" - output += "
" - - // If there are any upcoming events - if len(events[:pastEventsIdx]) > 0 { - for _, e := range events[:pastEventsIdx] { - output += e.Render(admin) - } - } else { - output += "No upcoming events.\n\n" - } - - output += "
\n\n" - output += "---\n\n" - - // Add past events - output += "## Past events\n\n" - output += "
" - - if len(events[pastEventsIdx:]) > 0 { - for _, e := range events[pastEventsIdx:] { - output += e.Render(admin) - } - } else { - output += "No past events.\n\n" - } - - output += "
\n\n" - - output += "---\n\n" - output += "#### *All times are local." - - return output -} - -func (e Event) Render(admin bool) string { - var buf bytes.Buffer - - buf.WriteString("
\n\n") - buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) - buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) - buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Start time:** %s\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"))) - buf.WriteString(ufmt.Sprintf("**End time:** %s\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"))) - - if admin { - buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) - buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DelEvent&id=%s)\n\n", e.id)) - } - - if e.link != "" { - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) - } - - buf.WriteString("
") - - return buf.String() -} - -func Render(path string) string { - if path == "admin" { - return renderHome(true) - } - //out := "" - // - //for _, e := range events { - // out += e.Render(false) - //} - //return out - return renderHome(false) -} - -func GetOwner() std.Address { - return auth.Owner() -} - -func AddModerator(mod std.Address) { - auth.AssertCallerIsOwner() - - if err := auth.AddToAuthList(mod); err != nil { - panic(err) - } -} - func GetEventByID(id seqid.ID) (*Event, int, error) { for i, event := range events { if event.id == id { diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno new file mode 100644 index 00000000000..f0e02bced6e --- /dev/null +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -0,0 +1,122 @@ +package events + +import ( + "bytes" + "sort" + "strconv" + "time" + + "gno.land/p/demo/ufmt" +) + +const ( + maxWidgetSize = 5 +) + +// RenderEventWidget shows up to amt of the latest events to auth caller +func RenderEventWidget(eventsToRender int) string { + numOfEvents := len(events) + if numOfEvents == 0 { + return "No events." + } + + if eventsToRender > 5 { + panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) + } + + if eventsToRender < 1 { + panic("you need to request at least 1 event to render") + } + + if eventsToRender > numOfEvents { + eventsToRender = numOfEvents + } + + output := "" + + for _, event := range events[numOfEvents-eventsToRender:] { + if event.startTime.After(time.Now()) { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + } + } + + return output +} + +func renderHome(admin bool) string { + output := "# gno.land events\n\n" + + if len(events) == 0 { + output += "No upcoming or past events." + return output + } + + output += "Below is auth list of all upcoming and past gno.land events.\n\n" + output += ufmt.Sprintf(time.Now().Format(time.RFC850)) + + output += "---\n\n" + + // Find position of the latest past event + pastEventsIdx := sort.Search(len(events), func(i int) bool { + return events[i].startTime.Before(time.Now()) + }) + + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" + + // If there are any upcoming events + if len(events[:pastEventsIdx]) > 0 { + for _, e := range events[:pastEventsIdx] { + output += e.Render(admin) + } + } else { + output += "No upcoming events.\n\n" + } + + output += "
\n\n" + output += "---\n\n" + + // Add past events + output += "## Past events\n\n" + output += "
" + + if len(events[pastEventsIdx:]) > 0 { + for _, e := range events[pastEventsIdx:] { + output += e.Render(admin) + } + } else { + output += "No past events.\n\n" + } + + output += "
\n\n" + + output += "---\n\n" + output += "#### *All times are local." + + return output +} + +func (e Event) Render(admin bool) string { + var buf bytes.Buffer + + buf.WriteString("
\n\n") + buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) + buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) + buf.WriteString(ufmt.Sprintf("**Start time:** %s\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"))) + buf.WriteString(ufmt.Sprintf("**End time:** %s\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"))) + + if admin { + buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DelEvent&id=%s)\n\n", e.id)) + } + + if e.link != "" { + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + } + + buf.WriteString("
") + + return buf.String() +} From 0852ad2fec92da292b05c7715745ecb168e0a2ee Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 02:02:53 +0200 Subject: [PATCH 46/60] update home test --- examples/gno.land/r/gnoland/home/overide_filetest.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/home/overide_filetest.gno b/examples/gno.land/r/gnoland/home/overide_filetest.gno index 34356b93349..4f21b90a3c2 100644 --- a/examples/gno.land/r/gnoland/home/overide_filetest.gno +++ b/examples/gno.land/r/gnoland/home/overide_filetest.gno @@ -21,4 +21,4 @@ func main() { // Output: // Hello World! -// r: unauthorized; caller is not owner +// r: ownable: caller is not owner From 2a24cfe489630f0455435819ed546d609301e3b5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 7 Aug 2024 02:17:55 +0200 Subject: [PATCH 47/60] delevent --- examples/gno.land/r/gnoland/events/events.gno | 4 ++-- examples/gno.land/r/gnoland/events/events_test.gno | 2 +- examples/gno.land/r/gnoland/events/rendering.gno | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 69ae7d497b3..90bac94bdb0 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -86,8 +86,8 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 return id } -// DelEvent deletes an event with auth given ID -func DelEvent(id seqid.ID) { +// DeleteEvent deletes an event with auth given ID +func DeleteEvent(id seqid.ID) { auth.AssertOnAuthList() e, idx, err := GetEventByID(id) diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index e0fe3079d53..39fd234d291 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -95,7 +95,7 @@ func TestDelEvent(t *testing.T) { t.Fatalf("Expected to find ToDelete event in render") } - DelEvent(id) + DeleteEvent(id) got = renderHome(false) if strings.Contains(got, "ToDelete") { diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index f0e02bced6e..e6b36c1536c 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -109,7 +109,7 @@ func (e Event) Render(admin bool) string { if admin { buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) - buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DelEvent&id=%s)\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DeleteEvent&id=%s)\n\n", e.id)) } if e.link != "" { From ecf3b041093f67ed9e98a9f6a428b268f64366da Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 8 Aug 2024 13:24:07 +0200 Subject: [PATCH 48/60] replace panic with errors in rendering --- examples/gno.land/r/gnoland/events/errors.gno | 7 ++++++- examples/gno.land/r/gnoland/events/rendering.gno | 11 +++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 1179882ba9b..cd7774dd5c5 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -1,10 +1,15 @@ package events -import "errors" +import ( + "errors" + "strconv" +) var ( ErrEmptyName = errors.New("event name cannot be empty") ErrInvalidStartTime = errors.New("event start time cannot be less than 0") ErrInvalidEndTime = errors.New("event end time cannot be before start time") ErrNoSuchID = errors.New("event with specified ID does not exist") + ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") + ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) ) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index e6b36c1536c..a0a5ddbb3b9 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -3,7 +3,6 @@ package events import ( "bytes" "sort" - "strconv" "time" "gno.land/p/demo/ufmt" @@ -14,18 +13,18 @@ const ( ) // RenderEventWidget shows up to amt of the latest events to auth caller -func RenderEventWidget(eventsToRender int) string { +func RenderEventWidget(eventsToRender int) (string, error) { numOfEvents := len(events) if numOfEvents == 0 { - return "No events." + return "No events.", nil } if eventsToRender > 5 { - panic("maximum number of events in widget is " + strconv.Itoa(maxWidgetSize)) + return "", ErrWidgetMaxAmt } if eventsToRender < 1 { - panic("you need to request at least 1 event to render") + return "", ErrWidgetMinAmt } if eventsToRender > numOfEvents { @@ -40,7 +39,7 @@ func RenderEventWidget(eventsToRender int) string { } } - return output + return output, nil } func renderHome(admin bool) string { From c3bb3e915c44ec44c4f41e29ac7e7952b0bff844 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 8 Aug 2024 22:55:02 +0200 Subject: [PATCH 49/60] wip save --- examples/gno.land/r/gnoland/events/admin.gno | 5 +- examples/gno.land/r/gnoland/events/errors.gno | 10 +-- examples/gno.land/r/gnoland/events/events.gno | 54 ++++++------ .../gno.land/r/gnoland/events/events_test.gno | 2 +- .../gno.land/r/gnoland/events/rendering.gno | 86 +++++++++++++------ 5 files changed, 94 insertions(+), 63 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/admin.gno b/examples/gno.land/r/gnoland/events/admin.gno index 550ed6bf3bf..fc9944b44c1 100644 --- a/examples/gno.land/r/gnoland/events/admin.gno +++ b/examples/gno.land/r/gnoland/events/admin.gno @@ -6,7 +6,10 @@ import ( "gno.land/p/demo/ownable/exts/authorizable" ) -var auth *authorizable.Authorizable +var ( + su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn + auth = authorizable.NewAuthorizableWithAddress(su) +) func GetOwner() std.Address { return auth.Owner() diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index cd7774dd5c5..a209100d0fc 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -6,10 +6,8 @@ import ( ) var ( - ErrEmptyName = errors.New("event name cannot be empty") - ErrInvalidStartTime = errors.New("event start time cannot be less than 0") - ErrInvalidEndTime = errors.New("event end time cannot be before start time") - ErrNoSuchID = errors.New("event with specified ID does not exist") - ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") - ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) + ErrEmptyName = errors.New("event name cannot be empty") + ErrNoSuchID = errors.New("event with specified ID does not exist") + ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") + ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 90bac94bdb0..04319afa5c7 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -6,25 +6,26 @@ import ( "strings" "time" - "gno.land/p/demo/ownable/exts/authorizable" "gno.land/p/demo/seqid" "gno.land/p/demo/ufmt" ) -type Event struct { - id seqid.ID - name string // name of event - description string // short description of event - link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page - location string // location of the event - startTime time.Time // start time of the event - endTime time.Time // end time of the event -} +type ( + Event struct { + id seqid.ID + name string // name of event + description string // short description of event + link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page + location string // location of the event + startTime time.Time // given in RFC3339 + endTime time.Time // end time of the event, given in RFC3339 + } -type eventsSlice []*Event + eventsSlice []*Event +) var ( - events eventsSlice // oldest event has the largest index + events = make(eventsSlice, 0) // oldest event has the largest index idCounter seqid.ID ) @@ -35,32 +36,29 @@ const ( EventEdited = "EventEdited" ) -func init() { - su := std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn - auth = authorizable.NewAuthorizableWithAddress(su) - - events = make(eventsSlice, 0) -} - // AddEvent adds auth new event // Start time & end time need to be specified in unix seconds -func AddEvent(name, description, link, location string, startTime, endTime int64) seqid.ID { +func AddEvent(name, description, link, location, st, et string) seqid.ID { auth.AssertOnAuthList() if strings.TrimSpace(name) == "" { panic(ErrEmptyName) } - if startTime <= 0 { - panic(ErrInvalidStartTime) + if len(description) > maxDescLength { + str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) + panic(str) } - if endTime <= startTime { - panic(ErrInvalidEndTime) + startTime, err := time.Parse(time.RFC3339, st) + if err != nil { + str := ufmt.Sprintf("invalid start time: %v", err) + panic(str) } - if len(description) > maxDescLength { - str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) + endTime, err := time.Parse(time.RFC3339, et) + if err != nil { + str := ufmt.Sprintf("invalid end time: %v", err) panic(str) } @@ -71,8 +69,8 @@ func AddEvent(name, description, link, location string, startTime, endTime int64 description: description, link: link, location: location, - startTime: time.Unix(startTime, 0), // given in unix seconds - endTime: time.Unix(endTime, 0), // given in unix seconds + startTime: startTime, + endTime: endTime, } events = append(events, e) diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 39fd234d291..f76fe35f6d5 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -85,7 +85,7 @@ func TestAddEventErrors(t *testing.T) { }) } -func TestDelEvent(t *testing.T) { +func TestDeleteEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index a0a5ddbb3b9..fc914f6a0a9 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -2,7 +2,6 @@ package events import ( "bytes" - "sort" "time" "gno.land/p/demo/ufmt" @@ -50,28 +49,33 @@ func renderHome(admin bool) string { return output } - output += "Below is auth list of all upcoming and past gno.land events.\n\n" - output += ufmt.Sprintf(time.Now().Format(time.RFC850)) + output += "Below is auth list of all gno.land events.\n\n" output += "---\n\n" - // Find position of the latest past event - pastEventsIdx := sort.Search(len(events), func(i int) bool { - return events[i].startTime.Before(time.Now()) - }) + //// Find position of the latest past event + //pastEventsIdx := sort.Search(len(events), func(i int) bool { + // return events[i].startTime.Before(time.Now()) + //}) + + output += "## Currently in progress\n\n" + output += "
" + + output += "
\n\n" + output += "---\n\n" // Add upcoming events output += "## Upcoming events\n\n" output += "
" // If there are any upcoming events - if len(events[:pastEventsIdx]) > 0 { - for _, e := range events[:pastEventsIdx] { - output += e.Render(admin) - } - } else { - output += "No upcoming events.\n\n" - } + //if len(events[:pastEventsIdx]) > 0 { + // for _, e := range events[:pastEventsIdx] { + // output += e.Render(admin) + // } + //} else { + // output += "No upcoming events.\n\n" + //} output += "
\n\n" output += "---\n\n" @@ -80,19 +84,16 @@ func renderHome(admin bool) string { output += "## Past events\n\n" output += "
" - if len(events[pastEventsIdx:]) > 0 { - for _, e := range events[pastEventsIdx:] { - output += e.Render(admin) - } - } else { - output += "No past events.\n\n" - } - + // + //if len(events[pastEventsIdx:]) > 0 { + // for _, e := range events[pastEventsIdx:] { + // output += e.Render(admin) + // } + //} else { + // output += "No past events.\n\n" + //} output += "
\n\n" - output += "---\n\n" - output += "#### *All times are local." - return output } @@ -103,8 +104,21 @@ func (e Event) Render(admin bool) string { buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - buf.WriteString(ufmt.Sprintf("**Start time:** %s\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"))) - buf.WriteString(ufmt.Sprintf("**End time:** %s\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"))) + + _, offset := e.startTime.Zone() // offset is in seconds + buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), offset/(60*60))) + + now := time.Now() + buf.WriteString(ufmt.Sprintf("now: %d\n\n", now.Unix())) + buf.WriteString(ufmt.Sprintf("readable now: %s\n\n", now.Format("02 Jan 2006, 03:04 PM"))) + buf.WriteString(ufmt.Sprintf("evtTime: %d\n\n", e.startTime.Unix())) + buf.WriteString(ufmt.Sprintf("diff: %d\n\n", now.Unix()-e.startTime.Unix())) + + if now.Before(e.startTime) { + buf.WriteString("ITS IN THE FUTURE!!!!\n\n") + } else { + buf.WriteString("its not in the future :c\n\n") + } if admin { buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) @@ -119,3 +133,21 @@ func (e Event) Render(admin bool) string { return buf.String() } + +func Render(path string) string { + if path == "admin" { + return renderHome(true) + } + + return renderHome(false) + //out := "" + //if len(events) != 0 { + // for _, e := range events { + // out += e.Render(false) + // } + // + // return out + //} + // + //return "no evnts" +} From be2a6d1466f712e6b6af9590477b4dbcee6601e4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Thu, 8 Aug 2024 22:55:19 +0200 Subject: [PATCH 50/60] update home --- examples/gno.land/r/gnoland/home/home.gno | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 68d18b7b061..4eda3a46693 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -83,9 +83,13 @@ func lastContributions(limit int) ui.Element { } func upcomingEvents(limit int) ui.Element { + out, err := events.RenderEventWidget(5) + if err != nil { + panic("sadsa") + } return ui.Element{ ui.H3("[Upcoming Events](/r/gnoland/events)"), - ui.Text(events.RenderEventWidget(5)), + ui.Text(out), } } From cb920159f4787b1f3bee015b922c6bfa5a7baf56 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 16:38:33 +0200 Subject: [PATCH 51/60] update rendering, update errors, remove panics --- .../events/{admin.gno => administration.gno} | 0 examples/gno.land/r/gnoland/events/errors.gno | 12 +- examples/gno.land/r/gnoland/events/events.gno | 123 +++++++++--------- .../gno.land/r/gnoland/events/events_test.gno | 1 - .../gno.land/r/gnoland/events/rendering.gno | 106 +++++++-------- 5 files changed, 120 insertions(+), 122 deletions(-) rename examples/gno.land/r/gnoland/events/{admin.gno => administration.gno} (100%) diff --git a/examples/gno.land/r/gnoland/events/admin.gno b/examples/gno.land/r/gnoland/events/administration.gno similarity index 100% rename from examples/gno.land/r/gnoland/events/admin.gno rename to examples/gno.land/r/gnoland/events/administration.gno diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index a209100d0fc..3b7278cd369 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -6,8 +6,12 @@ import ( ) var ( - ErrEmptyName = errors.New("event name cannot be empty") - ErrNoSuchID = errors.New("event with specified ID does not exist") - ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") - ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) + ErrEmptyName = errors.New("event name cannot be empty") + ErrNoSuchID = errors.New("event with specified ID does not exist") + ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") + ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) + ErrDescriptionTooLong = errors.New("event description is too long") + ErrInvalidStartTime = errors.New("invalid start time format") + ErrInvalidEndTime = errors.New("invalid end time format") + ErrStartEndTimezonemMismatch = errors.New("start and end timezones are not the same") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 04319afa5c7..7245abbd47d 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -25,7 +25,7 @@ type ( ) var ( - events = make(eventsSlice, 0) // oldest event has the largest index + events = make(eventsSlice, 0) idCounter seqid.ID ) @@ -37,29 +37,32 @@ const ( ) // AddEvent adds auth new event -// Start time & end time need to be specified in unix seconds -func AddEvent(name, description, link, location, st, et string) seqid.ID { +// Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00 +func AddEvent(name, description, link, location, startTime, endTime string) (int64, error) { auth.AssertOnAuthList() if strings.TrimSpace(name) == "" { - panic(ErrEmptyName) + return -1, ErrEmptyName } if len(description) > maxDescLength { - str := ufmt.Sprintf("event description is %d chars, while it cannot be more than %d chars", len(description), maxDescLength) - panic(str) + return -1, ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) } - startTime, err := time.Parse(time.RFC3339, st) + st, err := time.Parse(time.RFC3339, startTime) if err != nil { - str := ufmt.Sprintf("invalid start time: %v", err) - panic(str) + return -1, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err) } - endTime, err := time.Parse(time.RFC3339, et) + et, err := time.Parse(time.RFC3339, endTime) if err != nil { - str := ufmt.Sprintf("invalid end time: %v", err) - panic(str) + return -1, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err) + } + + _, stOffset := st.Zone() + _, etOffset := et.Zone() + if stOffset != etOffset { + return -1, ErrStartEndTimezonemMismatch } id := idCounter.Next() @@ -69,8 +72,8 @@ func AddEvent(name, description, link, location, st, et string) seqid.ID { description: description, link: link, location: location, - startTime: startTime, - endTime: endTime, + startTime: st, + endTime: et, } events = append(events, e) @@ -81,7 +84,7 @@ func AddEvent(name, description, link, location, st, et string) seqid.ID { e.id.String(), ) - return id + return int64(id), nil } // DeleteEvent deletes an event with auth given ID @@ -103,51 +106,51 @@ func DeleteEvent(id seqid.ID) { // EditEvent edits an event with auth given ID // It only updates values corresponding to non-empty/non-nil arguments sent with the call -func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { - auth.AssertOnAuthList() - - e, _, err := GetEventByID(id) - if err != nil { - panic(err) - } - - // Set only valid values - if strings.TrimSpace(name) != "" { - e.name = name - } - - if strings.TrimSpace(description) != "" { - e.description = description - } - - if strings.TrimSpace(link) != "" { - e.link = link - } - - if strings.TrimSpace(location) != "" { - e.location = location - } - - if startTime > 0 { - e.startTime = time.Unix(startTime, 0) - } - - if endTime < 0 || endTime < startTime { - panic(ErrInvalidEndTime) - } - - e.endTime = time.Unix(endTime, 0) - - std.Emit(EventEdited, - "id", - e.id.String(), - ) - - // If sort order was disrupted, sort again - if startTime > 0 { - sort.Sort(events) - } -} +//func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { +// auth.AssertOnAuthList() +// +// e, _, err := GetEventByID(id) +// if err != nil { +// panic(err) +// } +// +// // Set only valid values +// if strings.TrimSpace(name) != "" { +// e.name = name +// } +// +// if strings.TrimSpace(description) != "" { +// e.description = description +// } +// +// if strings.TrimSpace(link) != "" { +// e.link = link +// } +// +// if strings.TrimSpace(location) != "" { +// e.location = location +// } +// +// if startTime > 0 { +// e.startTime = time.Unix(startTime, 0) +// } +// +// if endTime < 0 || endTime < startTime { +// panic(ErrInvalidEndTime) +// } +// +// e.endTime = time.Unix(endTime, 0) +// +// std.Emit(EventEdited, +// "id", +// e.id.String(), +// ) +// +// // If sort order was disrupted, sort again +// if startTime > 0 { +// sort.Sort(events) +// } +//} func GetEventByID(id seqid.ID) (*Event, int, error) { for i, event := range events { diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index f76fe35f6d5..2e0120cf61e 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -10,7 +10,6 @@ import ( ) var ( - su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn suRealm = std.NewUserRealm(su) ) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index fc914f6a0a9..7f9c29e4143 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -49,50 +49,57 @@ func renderHome(admin bool) string { return output } - output += "Below is auth list of all gno.land events.\n\n" + output += "Below is a list of all gno.land events, including in progress, upcoming, and past ones.\n\n" output += "---\n\n" - //// Find position of the latest past event - //pastEventsIdx := sort.Search(len(events), func(i int) bool { - // return events[i].startTime.Before(time.Now()) - //}) + var ( + inProgress = "" + upcoming = "" + past = "" + now = time.Now() + ) + + for _, e := range events { + if now.Before(e.startTime) { + upcoming += e.Render(admin) + } else if now.After(e.endTime) { + past += e.Render(admin) + } else { + inProgress += e.Render(admin) + } + } - output += "## Currently in progress\n\n" - output += "
" + if upcoming != "" { + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" - output += "
\n\n" - output += "---\n\n" + output += upcoming - // Add upcoming events - output += "## Upcoming events\n\n" - output += "
" + output += "
\n\n" + output += "---\n\n" + } - // If there are any upcoming events - //if len(events[:pastEventsIdx]) > 0 { - // for _, e := range events[:pastEventsIdx] { - // output += e.Render(admin) - // } - //} else { - // output += "No upcoming events.\n\n" - //} + if inProgress != "" { + output += "## Currently in progress\n\n" + output += "
" - output += "
\n\n" - output += "---\n\n" + output += inProgress - // Add past events - output += "## Past events\n\n" - output += "
" + output += "
\n\n" + output += "---\n\n" + } + + if past != "" { + // Add past events + output += "## Past events\n\n" + output += "
" - // - //if len(events[pastEventsIdx:]) > 0 { - // for _, e := range events[pastEventsIdx:] { - // output += e.Render(admin) - // } - //} else { - // output += "No past events.\n\n" - //} - output += "
\n\n" + output += past + + output += "
\n\n" + } return output } @@ -106,27 +113,22 @@ func (e Event) Render(admin bool) string { buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) _, offset := e.startTime.Zone() // offset is in seconds - buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), offset/(60*60))) - - now := time.Now() - buf.WriteString(ufmt.Sprintf("now: %d\n\n", now.Unix())) - buf.WriteString(ufmt.Sprintf("readable now: %s\n\n", now.Format("02 Jan 2006, 03:04 PM"))) - buf.WriteString(ufmt.Sprintf("evtTime: %d\n\n", e.startTime.Unix())) - buf.WriteString(ufmt.Sprintf("diff: %d\n\n", now.Unix()-e.startTime.Unix())) - - if now.Before(e.startTime) { - buf.WriteString("ITS IN THE FUTURE!!!!\n\n") - } else { - buf.WriteString("its not in the future :c\n\n") + hoursOffset := offset / (60 * 60) + sign := "" + if offset >= 0 { + sign = "+" } + buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + buf.WriteString(ufmt.Sprintf("**End time:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + if admin { buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DeleteEvent&id=%s)\n\n", e.id)) } if e.link != "" { - buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + buf.WriteString(ufmt.Sprintf("[Website](%s)\n\n", e.link)) } buf.WriteString("
") @@ -140,14 +142,4 @@ func Render(path string) string { } return renderHome(false) - //out := "" - //if len(events) != 0 { - // for _, e := range events { - // out += e.Render(false) - // } - // - // return out - //} - // - //return "no evnts" } From 91bea8b67b514d5d94b7664972762fddc6be3662 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 17:12:58 +0200 Subject: [PATCH 52/60] update widget, rendering, home --- examples/gno.land/r/gnoland/events/events.gno | 2 +- examples/gno.land/r/gnoland/events/rendering.gno | 6 +++--- examples/gno.land/r/gnoland/home/home.gno | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 7245abbd47d..afa00b9ae39 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -25,7 +25,7 @@ type ( ) var ( - events = make(eventsSlice, 0) + events = make(eventsSlice, 0) // sorted idCounter seqid.ID ) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index 7f9c29e4143..273654ea245 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -11,14 +11,14 @@ const ( maxWidgetSize = 5 ) -// RenderEventWidget shows up to amt of the latest events to auth caller +// RenderEventWidget shows up to eventsToRender of the latest events to auth caller func RenderEventWidget(eventsToRender int) (string, error) { numOfEvents := len(events) if numOfEvents == 0 { return "No events.", nil } - if eventsToRender > 5 { + if eventsToRender > maxWidgetSize { return "", ErrWidgetMaxAmt } @@ -32,7 +32,7 @@ func RenderEventWidget(eventsToRender int) (string, error) { output := "" - for _, event := range events[numOfEvents-eventsToRender:] { + for _, event := range events[eventsToRender:] { if event.startTime.After(time.Now()) { output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) } diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 4eda3a46693..2251f764c49 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -83,9 +83,9 @@ func lastContributions(limit int) ui.Element { } func upcomingEvents(limit int) ui.Element { - out, err := events.RenderEventWidget(5) + out, err := events.RenderEventWidget(limit) if err != nil { - panic("sadsa") + panic(err) } return ui.Element{ ui.H3("[Upcoming Events](/r/gnoland/events)"), From 5c33ebafcc32e5760a1cfa46ba7ab22caa5a7d04 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 17:13:26 +0200 Subject: [PATCH 53/60] help git diff --- .../r/gnoland/events/administration.gno | 24 --- examples/gno.land/r/gnoland/events/errors.gno | 17 -- examples/gno.land/r/gnoland/events/events.gno | 179 ------------------ .../gno.land/r/gnoland/events/events_test.gno | 141 -------------- examples/gno.land/r/gnoland/events/gno.mod | 8 - .../gno.land/r/gnoland/events/rendering.gno | 145 -------------- 6 files changed, 514 deletions(-) delete mode 100644 examples/gno.land/r/gnoland/events/administration.gno delete mode 100644 examples/gno.land/r/gnoland/events/errors.gno delete mode 100644 examples/gno.land/r/gnoland/events/events.gno delete mode 100644 examples/gno.land/r/gnoland/events/events_test.gno delete mode 100644 examples/gno.land/r/gnoland/events/gno.mod delete mode 100644 examples/gno.land/r/gnoland/events/rendering.gno diff --git a/examples/gno.land/r/gnoland/events/administration.gno b/examples/gno.land/r/gnoland/events/administration.gno deleted file mode 100644 index fc9944b44c1..00000000000 --- a/examples/gno.land/r/gnoland/events/administration.gno +++ /dev/null @@ -1,24 +0,0 @@ -package events - -import ( - "std" - - "gno.land/p/demo/ownable/exts/authorizable" -) - -var ( - su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn - auth = authorizable.NewAuthorizableWithAddress(su) -) - -func GetOwner() std.Address { - return auth.Owner() -} - -func AddModerator(mod std.Address) { - auth.AssertCallerIsOwner() - - if err := auth.AddToAuthList(mod); err != nil { - panic(err) - } -} diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno deleted file mode 100644 index 3b7278cd369..00000000000 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ /dev/null @@ -1,17 +0,0 @@ -package events - -import ( - "errors" - "strconv" -) - -var ( - ErrEmptyName = errors.New("event name cannot be empty") - ErrNoSuchID = errors.New("event with specified ID does not exist") - ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") - ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) - ErrDescriptionTooLong = errors.New("event description is too long") - ErrInvalidStartTime = errors.New("invalid start time format") - ErrInvalidEndTime = errors.New("invalid end time format") - ErrStartEndTimezonemMismatch = errors.New("start and end timezones are not the same") -) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno deleted file mode 100644 index afa00b9ae39..00000000000 --- a/examples/gno.land/r/gnoland/events/events.gno +++ /dev/null @@ -1,179 +0,0 @@ -package events - -import ( - "sort" - "std" - "strings" - "time" - - "gno.land/p/demo/seqid" - "gno.land/p/demo/ufmt" -) - -type ( - Event struct { - id seqid.ID - name string // name of event - description string // short description of event - link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page - location string // location of the event - startTime time.Time // given in RFC3339 - endTime time.Time // end time of the event, given in RFC3339 - } - - eventsSlice []*Event -) - -var ( - events = make(eventsSlice, 0) // sorted - idCounter seqid.ID -) - -const ( - maxDescLength = 100 - EventAdded = "EventAdded" - EventDeleted = "EventDeleted" - EventEdited = "EventEdited" -) - -// AddEvent adds auth new event -// Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00 -func AddEvent(name, description, link, location, startTime, endTime string) (int64, error) { - auth.AssertOnAuthList() - - if strings.TrimSpace(name) == "" { - return -1, ErrEmptyName - } - - if len(description) > maxDescLength { - return -1, ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) - } - - st, err := time.Parse(time.RFC3339, startTime) - if err != nil { - return -1, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err) - } - - et, err := time.Parse(time.RFC3339, endTime) - if err != nil { - return -1, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err) - } - - _, stOffset := st.Zone() - _, etOffset := et.Zone() - if stOffset != etOffset { - return -1, ErrStartEndTimezonemMismatch - } - - id := idCounter.Next() - e := &Event{ - id: id, - name: name, - description: description, - link: link, - location: location, - startTime: st, - endTime: et, - } - - events = append(events, e) - sort.Sort(events) - - std.Emit(EventAdded, - "id", - e.id.String(), - ) - - return int64(id), nil -} - -// DeleteEvent deletes an event with auth given ID -func DeleteEvent(id seqid.ID) { - auth.AssertOnAuthList() - - e, idx, err := GetEventByID(id) - if err != nil { - panic(err) - } - - events = append(events[:idx], events[idx+1:]...) - - std.Emit(EventDeleted, - "id", - e.id.String(), - ) -} - -// EditEvent edits an event with auth given ID -// It only updates values corresponding to non-empty/non-nil arguments sent with the call -//func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { -// auth.AssertOnAuthList() -// -// e, _, err := GetEventByID(id) -// if err != nil { -// panic(err) -// } -// -// // Set only valid values -// if strings.TrimSpace(name) != "" { -// e.name = name -// } -// -// if strings.TrimSpace(description) != "" { -// e.description = description -// } -// -// if strings.TrimSpace(link) != "" { -// e.link = link -// } -// -// if strings.TrimSpace(location) != "" { -// e.location = location -// } -// -// if startTime > 0 { -// e.startTime = time.Unix(startTime, 0) -// } -// -// if endTime < 0 || endTime < startTime { -// panic(ErrInvalidEndTime) -// } -// -// e.endTime = time.Unix(endTime, 0) -// -// std.Emit(EventEdited, -// "id", -// e.id.String(), -// ) -// -// // If sort order was disrupted, sort again -// if startTime > 0 { -// sort.Sort(events) -// } -//} - -func GetEventByID(id seqid.ID) (*Event, int, error) { - for i, event := range events { - if event.id == id { - return event, i, nil - } - } - - return nil, -1, ErrNoSuchID -} - -// Len returns the length of the slice -func (m eventsSlice) Len() int { - return len(m) -} - -// Less compares the startTime fields of two elements -// In this case, events will be sorted by largest startTime first (upcoming > past) -func (m eventsSlice) Less(i, j int) bool { - return m[i].startTime.After(m[j].startTime) -} - -// Swap swaps two elements in the slice -func (m eventsSlice) Swap(i, j int) { - m[i], m[j] = m[j], m[i] -} diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno deleted file mode 100644 index 2e0120cf61e..00000000000 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ /dev/null @@ -1,141 +0,0 @@ -package events - -import ( - "std" - "strings" - "testing" - "time" - - "gno.land/p/demo/uassert" -) - -var ( - suRealm = std.NewUserRealm(su) -) - -func TestAddEvent(t *testing.T) { - std.TestSetOrigCaller(su) - std.TestSetRealm(suRealm) - - now := time.Now() - - e1Start := now.Add(time.Hour * 24 * 5) - e1End := e1Start.Add(time.Hour * 4) - AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Unix(), e1End.Unix()) - - got := renderHome(false) - - if !strings.Contains(got, "Event 1") { - t.Fatalf("Expected to find Event 1 in render") - } - - e2Start := now.Add(-time.Hour * 24 * 5) - e2End := e2Start.Add(time.Hour * 4) - - AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Unix(), e2End.Unix()) - - got = renderHome(false) - - upcomingPos := strings.Index(got, "## Upcoming events") - pastPos := strings.Index(got, "## Past events") - - e1Pos := strings.Index(got, "Event 1") - e2Pos := strings.Index(got, "Event 2") - - // expected index ordering: upcoming < e1 < past < e2 - if e1Pos < upcomingPos || e1Pos > pastPos { - t.Fatalf("Expected to find Event 1 in Upcoming events") - } - - if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { - t.Fatalf("Expected to find Event 2 on auth different pos") - } - - // larger index => smaller startTime (future => past) - if events[0].startTime.Unix() < events[1].startTime.Unix() { - t.Fatalf("expected ordering to be different") - } -} - -func TestAddEventErrors(t *testing.T) { - std.TestSetOrigCaller(su) - std.TestSetRealm(suRealm) - - uassert.PanicsWithMessage(t, ErrEmptyName.Error(), func() { - AddEvent("", "sample desc", "gno.land", "gnome land", 123, 456) - }) - - uassert.PanicsWithMessage(t, ErrInvalidStartTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 0, 456) - }) - - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 0) - }) - - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 1) - }) - - tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` - // figure out auth way to check panics with dynamic params in auth better way - uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { - AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) - }) -} - -func TestDeleteEvent(t *testing.T) { - events = nil // remove elements from previous tests - see issue #1982 - - id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) - got := renderHome(false) - - if !strings.Contains(got, "ToDelete") { - t.Fatalf("Expected to find ToDelete event in render") - } - - DeleteEvent(id) - got = renderHome(false) - - if strings.Contains(got, "ToDelete") { - t.Fatalf("Did not expect to find ToDelete event in render") - } -} - -func TestEditEvent(t *testing.T) { - events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) - - newName := "New Name" - newDesc := "Normal description" - newST := int64(1234) - newET := int64(4567) - - EditEvent(id, newName, newDesc, "", "", newST, newET) - edited, _, _ := GetEventByID(id) - - if edited.name != newName { - t.Fatalf("Expected %s, got %s", newName, edited.name) - } - - if edited.description != newDesc { - t.Fatalf("Expected %s, got %s", newDesc, edited.description) - } - - if edited.startTime != time.Unix(newST, 0) { - t.Fatalf("Expected %d, got %d", newST, edited.startTime.Unix()) - } - - if edited.endTime != time.Unix(newET, 0) { - t.Fatalf("Expected %d, got %d", newET, edited.endTime.Unix()) - } -} - -func TestInvalidEdit(t *testing.T) { - events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) - - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - EditEvent(id, "", "", "", "", 0, -5) - }) -} diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod deleted file mode 100644 index 5a4c6ac56f3..00000000000 --- a/examples/gno.land/r/gnoland/events/gno.mod +++ /dev/null @@ -1,8 +0,0 @@ -module gno.land/r/gnoland/events - -require ( - gno.land/p/demo/ownable/exts/authorizable v0.0.0-latest - gno.land/p/demo/seqid v0.0.0-latest - gno.land/p/demo/uassert v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest -) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno deleted file mode 100644 index 273654ea245..00000000000 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ /dev/null @@ -1,145 +0,0 @@ -package events - -import ( - "bytes" - "time" - - "gno.land/p/demo/ufmt" -) - -const ( - maxWidgetSize = 5 -) - -// RenderEventWidget shows up to eventsToRender of the latest events to auth caller -func RenderEventWidget(eventsToRender int) (string, error) { - numOfEvents := len(events) - if numOfEvents == 0 { - return "No events.", nil - } - - if eventsToRender > maxWidgetSize { - return "", ErrWidgetMaxAmt - } - - if eventsToRender < 1 { - return "", ErrWidgetMinAmt - } - - if eventsToRender > numOfEvents { - eventsToRender = numOfEvents - } - - output := "" - - for _, event := range events[eventsToRender:] { - if event.startTime.After(time.Now()) { - output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) - } - } - - return output, nil -} - -func renderHome(admin bool) string { - output := "# gno.land events\n\n" - - if len(events) == 0 { - output += "No upcoming or past events." - return output - } - - output += "Below is a list of all gno.land events, including in progress, upcoming, and past ones.\n\n" - - output += "---\n\n" - - var ( - inProgress = "" - upcoming = "" - past = "" - now = time.Now() - ) - - for _, e := range events { - if now.Before(e.startTime) { - upcoming += e.Render(admin) - } else if now.After(e.endTime) { - past += e.Render(admin) - } else { - inProgress += e.Render(admin) - } - } - - if upcoming != "" { - // Add upcoming events - output += "## Upcoming events\n\n" - output += "
" - - output += upcoming - - output += "
\n\n" - output += "---\n\n" - } - - if inProgress != "" { - output += "## Currently in progress\n\n" - output += "
" - - output += inProgress - - output += "
\n\n" - output += "---\n\n" - } - - if past != "" { - // Add past events - output += "## Past events\n\n" - output += "
" - - output += past - - output += "
\n\n" - } - - return output -} - -func (e Event) Render(admin bool) string { - var buf bytes.Buffer - - buf.WriteString("
\n\n") - buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) - buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) - buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) - - _, offset := e.startTime.Zone() // offset is in seconds - hoursOffset := offset / (60 * 60) - sign := "" - if offset >= 0 { - sign = "+" - } - - buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) - buf.WriteString(ufmt.Sprintf("**End time:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) - - if admin { - buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) - buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DeleteEvent&id=%s)\n\n", e.id)) - } - - if e.link != "" { - buf.WriteString(ufmt.Sprintf("[Website](%s)\n\n", e.link)) - } - - buf.WriteString("
") - - return buf.String() -} - -func Render(path string) string { - if path == "admin" { - return renderHome(true) - } - - return renderHome(false) -} From c6a81f9cc173b82203a28899cc4b93ba109fddc6 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 17:13:44 +0200 Subject: [PATCH 54/60] add back events folder --- .../r/gnoland/events/administration.gno | 24 +++ examples/gno.land/r/gnoland/events/errors.gno | 17 ++ examples/gno.land/r/gnoland/events/events.gno | 179 ++++++++++++++++++ .../gno.land/r/gnoland/events/events_test.gno | 141 ++++++++++++++ examples/gno.land/r/gnoland/events/gno.mod | 3 + .../gno.land/r/gnoland/events/rendering.gno | 145 ++++++++++++++ 6 files changed, 509 insertions(+) create mode 100644 examples/gno.land/r/gnoland/events/administration.gno create mode 100644 examples/gno.land/r/gnoland/events/errors.gno create mode 100644 examples/gno.land/r/gnoland/events/events.gno create mode 100644 examples/gno.land/r/gnoland/events/events_test.gno create mode 100644 examples/gno.land/r/gnoland/events/gno.mod create mode 100644 examples/gno.land/r/gnoland/events/rendering.gno diff --git a/examples/gno.land/r/gnoland/events/administration.gno b/examples/gno.land/r/gnoland/events/administration.gno new file mode 100644 index 00000000000..fc9944b44c1 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/administration.gno @@ -0,0 +1,24 @@ +package events + +import ( + "std" + + "gno.land/p/demo/ownable/exts/authorizable" +) + +var ( + su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn + auth = authorizable.NewAuthorizableWithAddress(su) +) + +func GetOwner() std.Address { + return auth.Owner() +} + +func AddModerator(mod std.Address) { + auth.AssertCallerIsOwner() + + if err := auth.AddToAuthList(mod); err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno new file mode 100644 index 00000000000..3b7278cd369 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -0,0 +1,17 @@ +package events + +import ( + "errors" + "strconv" +) + +var ( + ErrEmptyName = errors.New("event name cannot be empty") + ErrNoSuchID = errors.New("event with specified ID does not exist") + ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") + ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) + ErrDescriptionTooLong = errors.New("event description is too long") + ErrInvalidStartTime = errors.New("invalid start time format") + ErrInvalidEndTime = errors.New("invalid end time format") + ErrStartEndTimezonemMismatch = errors.New("start and end timezones are not the same") +) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno new file mode 100644 index 00000000000..afa00b9ae39 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -0,0 +1,179 @@ +package events + +import ( + "sort" + "std" + "strings" + "time" + + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) + +type ( + Event struct { + id seqid.ID + name string // name of event + description string // short description of event + link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page + location string // location of the event + startTime time.Time // given in RFC3339 + endTime time.Time // end time of the event, given in RFC3339 + } + + eventsSlice []*Event +) + +var ( + events = make(eventsSlice, 0) // sorted + idCounter seqid.ID +) + +const ( + maxDescLength = 100 + EventAdded = "EventAdded" + EventDeleted = "EventDeleted" + EventEdited = "EventEdited" +) + +// AddEvent adds auth new event +// Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00 +func AddEvent(name, description, link, location, startTime, endTime string) (int64, error) { + auth.AssertOnAuthList() + + if strings.TrimSpace(name) == "" { + return -1, ErrEmptyName + } + + if len(description) > maxDescLength { + return -1, ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) + } + + st, err := time.Parse(time.RFC3339, startTime) + if err != nil { + return -1, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err) + } + + et, err := time.Parse(time.RFC3339, endTime) + if err != nil { + return -1, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err) + } + + _, stOffset := st.Zone() + _, etOffset := et.Zone() + if stOffset != etOffset { + return -1, ErrStartEndTimezonemMismatch + } + + id := idCounter.Next() + e := &Event{ + id: id, + name: name, + description: description, + link: link, + location: location, + startTime: st, + endTime: et, + } + + events = append(events, e) + sort.Sort(events) + + std.Emit(EventAdded, + "id", + e.id.String(), + ) + + return int64(id), nil +} + +// DeleteEvent deletes an event with auth given ID +func DeleteEvent(id seqid.ID) { + auth.AssertOnAuthList() + + e, idx, err := GetEventByID(id) + if err != nil { + panic(err) + } + + events = append(events[:idx], events[idx+1:]...) + + std.Emit(EventDeleted, + "id", + e.id.String(), + ) +} + +// EditEvent edits an event with auth given ID +// It only updates values corresponding to non-empty/non-nil arguments sent with the call +//func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { +// auth.AssertOnAuthList() +// +// e, _, err := GetEventByID(id) +// if err != nil { +// panic(err) +// } +// +// // Set only valid values +// if strings.TrimSpace(name) != "" { +// e.name = name +// } +// +// if strings.TrimSpace(description) != "" { +// e.description = description +// } +// +// if strings.TrimSpace(link) != "" { +// e.link = link +// } +// +// if strings.TrimSpace(location) != "" { +// e.location = location +// } +// +// if startTime > 0 { +// e.startTime = time.Unix(startTime, 0) +// } +// +// if endTime < 0 || endTime < startTime { +// panic(ErrInvalidEndTime) +// } +// +// e.endTime = time.Unix(endTime, 0) +// +// std.Emit(EventEdited, +// "id", +// e.id.String(), +// ) +// +// // If sort order was disrupted, sort again +// if startTime > 0 { +// sort.Sort(events) +// } +//} + +func GetEventByID(id seqid.ID) (*Event, int, error) { + for i, event := range events { + if event.id == id { + return event, i, nil + } + } + + return nil, -1, ErrNoSuchID +} + +// Len returns the length of the slice +func (m eventsSlice) Len() int { + return len(m) +} + +// Less compares the startTime fields of two elements +// In this case, events will be sorted by largest startTime first (upcoming > past) +func (m eventsSlice) Less(i, j int) bool { + return m[i].startTime.After(m[j].startTime) +} + +// Swap swaps two elements in the slice +func (m eventsSlice) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno new file mode 100644 index 00000000000..2e0120cf61e --- /dev/null +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -0,0 +1,141 @@ +package events + +import ( + "std" + "strings" + "testing" + "time" + + "gno.land/p/demo/uassert" +) + +var ( + suRealm = std.NewUserRealm(su) +) + +func TestAddEvent(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + now := time.Now() + + e1Start := now.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Unix(), e1End.Unix()) + + got := renderHome(false) + + if !strings.Contains(got, "Event 1") { + t.Fatalf("Expected to find Event 1 in render") + } + + e2Start := now.Add(-time.Hour * 24 * 5) + e2End := e2Start.Add(time.Hour * 4) + + AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Unix(), e2End.Unix()) + + got = renderHome(false) + + upcomingPos := strings.Index(got, "## Upcoming events") + pastPos := strings.Index(got, "## Past events") + + e1Pos := strings.Index(got, "Event 1") + e2Pos := strings.Index(got, "Event 2") + + // expected index ordering: upcoming < e1 < past < e2 + if e1Pos < upcomingPos || e1Pos > pastPos { + t.Fatalf("Expected to find Event 1 in Upcoming events") + } + + if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { + t.Fatalf("Expected to find Event 2 on auth different pos") + } + + // larger index => smaller startTime (future => past) + if events[0].startTime.Unix() < events[1].startTime.Unix() { + t.Fatalf("expected ordering to be different") + } +} + +func TestAddEventErrors(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + uassert.PanicsWithMessage(t, ErrEmptyName.Error(), func() { + AddEvent("", "sample desc", "gno.land", "gnome land", 123, 456) + }) + + uassert.PanicsWithMessage(t, ErrInvalidStartTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 0, 456) + }) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 0) + }) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 1) + }) + + tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` + // figure out auth way to check panics with dynamic params in auth better way + uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { + AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) + }) +} + +func TestDeleteEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) + got := renderHome(false) + + if !strings.Contains(got, "ToDelete") { + t.Fatalf("Expected to find ToDelete event in render") + } + + DeleteEvent(id) + got = renderHome(false) + + if strings.Contains(got, "ToDelete") { + t.Fatalf("Did not expect to find ToDelete event in render") + } +} + +func TestEditEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) + + newName := "New Name" + newDesc := "Normal description" + newST := int64(1234) + newET := int64(4567) + + EditEvent(id, newName, newDesc, "", "", newST, newET) + edited, _, _ := GetEventByID(id) + + if edited.name != newName { + t.Fatalf("Expected %s, got %s", newName, edited.name) + } + + if edited.description != newDesc { + t.Fatalf("Expected %s, got %s", newDesc, edited.description) + } + + if edited.startTime != time.Unix(newST, 0) { + t.Fatalf("Expected %d, got %d", newST, edited.startTime.Unix()) + } + + if edited.endTime != time.Unix(newET, 0) { + t.Fatalf("Expected %d, got %d", newET, edited.endTime.Unix()) + } +} + +func TestInvalidEdit(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) + + uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { + EditEvent(id, "", "", "", "", 0, -5) + }) +} diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod new file mode 100644 index 00000000000..ec781c7cf10 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/gno.mod @@ -0,0 +1,3 @@ +module gno.land/r/gnoland/events + +require gno.land/p/demo/ui v0.0.0-latest diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno new file mode 100644 index 00000000000..273654ea245 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -0,0 +1,145 @@ +package events + +import ( + "bytes" + "time" + + "gno.land/p/demo/ufmt" +) + +const ( + maxWidgetSize = 5 +) + +// RenderEventWidget shows up to eventsToRender of the latest events to auth caller +func RenderEventWidget(eventsToRender int) (string, error) { + numOfEvents := len(events) + if numOfEvents == 0 { + return "No events.", nil + } + + if eventsToRender > maxWidgetSize { + return "", ErrWidgetMaxAmt + } + + if eventsToRender < 1 { + return "", ErrWidgetMinAmt + } + + if eventsToRender > numOfEvents { + eventsToRender = numOfEvents + } + + output := "" + + for _, event := range events[eventsToRender:] { + if event.startTime.After(time.Now()) { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + } + } + + return output, nil +} + +func renderHome(admin bool) string { + output := "# gno.land events\n\n" + + if len(events) == 0 { + output += "No upcoming or past events." + return output + } + + output += "Below is a list of all gno.land events, including in progress, upcoming, and past ones.\n\n" + + output += "---\n\n" + + var ( + inProgress = "" + upcoming = "" + past = "" + now = time.Now() + ) + + for _, e := range events { + if now.Before(e.startTime) { + upcoming += e.Render(admin) + } else if now.After(e.endTime) { + past += e.Render(admin) + } else { + inProgress += e.Render(admin) + } + } + + if upcoming != "" { + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" + + output += upcoming + + output += "
\n\n" + output += "---\n\n" + } + + if inProgress != "" { + output += "## Currently in progress\n\n" + output += "
" + + output += inProgress + + output += "
\n\n" + output += "---\n\n" + } + + if past != "" { + // Add past events + output += "## Past events\n\n" + output += "
" + + output += past + + output += "
\n\n" + } + + return output +} + +func (e Event) Render(admin bool) string { + var buf bytes.Buffer + + buf.WriteString("
\n\n") + buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) + buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) + + _, offset := e.startTime.Zone() // offset is in seconds + hoursOffset := offset / (60 * 60) + sign := "" + if offset >= 0 { + sign = "+" + } + + buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + buf.WriteString(ufmt.Sprintf("**End time:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + + if admin { + buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DeleteEvent&id=%s)\n\n", e.id)) + } + + if e.link != "" { + buf.WriteString(ufmt.Sprintf("[Website](%s)\n\n", e.link)) + } + + buf.WriteString("
") + + return buf.String() +} + +func Render(path string) string { + if path == "admin" { + return renderHome(true) + } + + return renderHome(false) +} From 815b4823d2af52af140e9b48005ad31896256943 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 19:02:45 +0200 Subject: [PATCH 55/60] parseTimes, update home, update rendering --- examples/gno.land/r/gnoland/events/errors.gno | 3 +- examples/gno.land/r/gnoland/events/events.gno | 134 ++++++++++-------- examples/gno.land/r/gnoland/events/gno.mod | 7 +- .../gno.land/r/gnoland/events/rendering.gno | 9 +- 4 files changed, 86 insertions(+), 67 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno index 3b7278cd369..788115015a4 100644 --- a/examples/gno.land/r/gnoland/events/errors.gno +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -9,9 +9,10 @@ var ( ErrEmptyName = errors.New("event name cannot be empty") ErrNoSuchID = errors.New("event with specified ID does not exist") ErrWidgetMinAmt = errors.New("you need to request at least 1 event to render") - ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(maxWidgetSize)) + ErrWidgetMaxAmt = errors.New("maximum number of events in widget is" + strconv.Itoa(MaxWidgetSize)) ErrDescriptionTooLong = errors.New("event description is too long") ErrInvalidStartTime = errors.New("invalid start time format") ErrInvalidEndTime = errors.New("invalid end time format") + ErrEndBeforeStart = errors.New("end time cannot be before start time") ErrStartEndTimezonemMismatch = errors.New("start and end timezones are not the same") ) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index afa00b9ae39..fe383d123f2 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -49,20 +49,10 @@ func AddEvent(name, description, link, location, startTime, endTime string) (int return -1, ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) } - st, err := time.Parse(time.RFC3339, startTime) + // Parse times + st, et, err := parseTimes(startTime, endTime) if err != nil { - return -1, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err) - } - - et, err := time.Parse(time.RFC3339, endTime) - if err != nil { - return -1, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err) - } - - _, stOffset := st.Zone() - _, etOffset := et.Zone() - if stOffset != etOffset { - return -1, ErrStartEndTimezonemMismatch + return -1, err } id := idCounter.Next() @@ -105,52 +95,54 @@ func DeleteEvent(id seqid.ID) { } // EditEvent edits an event with auth given ID -// It only updates values corresponding to non-empty/non-nil arguments sent with the call -//func EditEvent(id seqid.ID, name, description, link, location string, startTime, endTime int64) { -// auth.AssertOnAuthList() -// -// e, _, err := GetEventByID(id) -// if err != nil { -// panic(err) -// } -// -// // Set only valid values -// if strings.TrimSpace(name) != "" { -// e.name = name -// } -// -// if strings.TrimSpace(description) != "" { -// e.description = description -// } -// -// if strings.TrimSpace(link) != "" { -// e.link = link -// } -// -// if strings.TrimSpace(location) != "" { -// e.location = location -// } -// -// if startTime > 0 { -// e.startTime = time.Unix(startTime, 0) -// } -// -// if endTime < 0 || endTime < startTime { -// panic(ErrInvalidEndTime) -// } -// -// e.endTime = time.Unix(endTime, 0) -// -// std.Emit(EventEdited, -// "id", -// e.id.String(), -// ) -// -// // If sort order was disrupted, sort again -// if startTime > 0 { -// sort.Sort(events) -// } -//} +// It only updates values corresponding to non-empty arguments sent with the call +// Note: if you need to update the start time or end time, you need to provide both every time +func EditEvent(id seqid.ID, name, description, link, location, startTime, endTime string) { + auth.AssertOnAuthList() + + e, _, err := GetEventByID(id) + if err != nil { + panic(err) + } + + // Set only valid values + if strings.TrimSpace(name) != "" { + e.name = name + } + + if strings.TrimSpace(description) != "" { + e.description = description + } + + if strings.TrimSpace(link) != "" { + e.link = link + } + + if strings.TrimSpace(location) != "" { + e.location = location + } + + if strings.TrimSpace(startTime) != "" || strings.TrimSpace(endTime) != "" { + st, et, err := parseTimes(startTime, endTime) + if err != nil { + panic(err) // need to also revert other state changes + } + + oldStartTime := e.startTime + e.startTime = st + e.endTime = et + + // If sort order was disrupted, sort again + if oldStartTime != e.startTime { + sort.Sort(events) + } + } + + std.Emit(EventEdited, + "id", + e.id.String(), + ) +} func GetEventByID(id seqid.ID) (*Event, int, error) { for i, event := range events { @@ -177,3 +169,27 @@ func (m eventsSlice) Less(i, j int) bool { func (m eventsSlice) Swap(i, j int) { m[i], m[j] = m[j], m[i] } + +func parseTimes(startTime, endTime string) (time.Time, time.Time, error) { + st, err := time.Parse(time.RFC3339, startTime) + if err != nil { + return time.Time{}, time.Time{}, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err.Error()) + } + + et, err := time.Parse(time.RFC3339, endTime) + if err != nil { + return time.Time{}, time.Time{}, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err.Error()) + } + + if et.Before(st) { + return time.Time{}, time.Time{}, ErrEndBeforeStart + } + + _, stOffset := st.Zone() + _, etOffset := et.Zone() + if stOffset != etOffset { + return time.Time{}, time.Time{}, ErrStartEndTimezonemMismatch + } + + return st, et, nil +} diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod index ec781c7cf10..5a4c6ac56f3 100644 --- a/examples/gno.land/r/gnoland/events/gno.mod +++ b/examples/gno.land/r/gnoland/events/gno.mod @@ -1,3 +1,8 @@ module gno.land/r/gnoland/events -require gno.land/p/demo/ui v0.0.0-latest +require ( + gno.land/p/demo/ownable/exts/authorizable v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index 273654ea245..3f54da47780 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -8,7 +8,7 @@ import ( ) const ( - maxWidgetSize = 5 + MaxWidgetSize = 5 ) // RenderEventWidget shows up to eventsToRender of the latest events to auth caller @@ -18,7 +18,7 @@ func RenderEventWidget(eventsToRender int) (string, error) { return "No events.", nil } - if eventsToRender > maxWidgetSize { + if eventsToRender > MaxWidgetSize { return "", ErrWidgetMaxAmt } @@ -33,9 +33,7 @@ func RenderEventWidget(eventsToRender int) (string, error) { output := "" for _, event := range events[eventsToRender:] { - if event.startTime.After(time.Now()) { - output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) - } + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) } return output, nil @@ -50,7 +48,6 @@ func renderHome(admin bool) string { } output += "Below is a list of all gno.land events, including in progress, upcoming, and past ones.\n\n" - output += "---\n\n" var ( From 3d643f0ea142caf9448fefde5bd020070766fac4 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 19:31:08 +0200 Subject: [PATCH 56/60] update tests, home --- .../gno.land/r/gnoland/events/events_test.gno | 85 ++++++++++++------- examples/gno.land/r/gnoland/home/home.gno | 9 +- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 2e0120cf61e..27b07e44ae1 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -6,22 +6,28 @@ import ( "testing" "time" + "gno.land/p/demo/seqid" "gno.land/p/demo/uassert" ) var ( suRealm = std.NewUserRealm(su) + now = "2009-02-13T23:31:30Z" // time.Now() is hardcoded to this value in the gno test machine currently ) func TestAddEvent(t *testing.T) { std.TestSetOrigCaller(su) std.TestSetRealm(suRealm) - now := time.Now() + parsedTimeNow, err := time.Parse(time.RFC3339, now) + if err != nil { + panic(err) + } - e1Start := now.Add(time.Hour * 24 * 5) + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) e1End := e1Start.Add(time.Hour * 4) - AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Unix(), e1End.Unix()) + + AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) got := renderHome(false) @@ -29,10 +35,10 @@ func TestAddEvent(t *testing.T) { t.Fatalf("Expected to find Event 1 in render") } - e2Start := now.Add(-time.Hour * 24 * 5) + e2Start := parsedTimeNow.Add(-time.Hour * 24 * 5) e2End := e2Start.Add(time.Hour * 4) - AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Unix(), e2End.Unix()) + AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Format(time.RFC3339), e2End.Format(time.RFC3339)) got = renderHome(false) @@ -61,40 +67,46 @@ func TestAddEventErrors(t *testing.T) { std.TestSetOrigCaller(su) std.TestSetRealm(suRealm) - uassert.PanicsWithMessage(t, ErrEmptyName.Error(), func() { - AddEvent("", "sample desc", "gno.land", "gnome land", 123, 456) - }) + _, err := AddEvent("", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:33:31Z") + uassert.ErrorIs(t, err, ErrEmptyName) - uassert.PanicsWithMessage(t, ErrInvalidStartTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 0, 456) - }) + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "", "2009-02-13T23:33:31Z") + uassert.ErrorContains(t, err, ErrInvalidStartTime.Error()) - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 0) - }) + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "") + uassert.ErrorContains(t, err, ErrInvalidEndTime.Error()) - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - AddEvent("sample name", "sample desc", "gno.land", "gnome land", 123, 1) - }) + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:30:31Z") + uassert.ErrorIs(t, err, ErrEndBeforeStart) + + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31+06:00", "2009-02-13T23:33:31+02:00") + uassert.ErrorIs(t, err, ErrStartEndTimezonemMismatch) tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` - // figure out auth way to check panics with dynamic params in auth better way - uassert.PanicsWithMessage(t, "event description is 101 chars, while it cannot be more than 100 chars", func() { - AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", 123, 456) - }) + _, err = AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:33:31Z") + uassert.ErrorContains(t, err, ErrDescriptionTooLong.Error()) } func TestDeleteEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToDelete", "sample desc", "gno.land", "gnome land", 123, 456) + parsedTimeNow, err := time.Parse(time.RFC3339, now) + if err != nil { + panic(err) + } + + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + + id, _ := AddEvent("ToDelete", "description", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + got := renderHome(false) if !strings.Contains(got, "ToDelete") { t.Fatalf("Expected to find ToDelete event in render") } - DeleteEvent(id) + DeleteEvent(seqid.ID(id)) got = renderHome(false) if strings.Contains(got, "ToDelete") { @@ -104,7 +116,16 @@ func TestDeleteEvent(t *testing.T) { func TestEditEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) + + parsedTimeNow, err := time.Parse(time.RFC3339, now) + if err != nil { + panic(err) + } + + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + + id, _ := AddEvent("ToDelete", "description", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) newName := "New Name" newDesc := "Normal description" @@ -131,11 +152,11 @@ func TestEditEvent(t *testing.T) { } } -func TestInvalidEdit(t *testing.T) { - events = nil // remove elements from previous tests - see issue #1982 - id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) - - uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { - EditEvent(id, "", "", "", "", 0, -5) - }) -} +//func TestInvalidEdit(t *testing.T) { +// events = nil // remove elements from previous tests - see issue #1982 +// id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) +// +// uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { +// EditEvent(id, "", "", "", "", 0, -5) +// }) +//} diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 2251f764c49..31ac910534a 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -36,7 +36,7 @@ func Render(_ string) string { dom.Body.Append( ui.Columns{3, []ui.Element{ lastBlogposts(4), - upcomingEvents(4), + upcomingEvents(), lastContributions(4), }}, ) @@ -82,11 +82,8 @@ func lastContributions(limit int) ui.Element { } } -func upcomingEvents(limit int) ui.Element { - out, err := events.RenderEventWidget(limit) - if err != nil { - panic(err) - } +func upcomingEvents() ui.Element { + out, _ := events.RenderEventWidget(events.MaxWidgetSize) return ui.Element{ ui.H3("[Upcoming Events](/r/gnoland/events)"), ui.Text(out), From 9ba3ea6245636fe9ed53cf716197743f39278632 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Fri, 9 Aug 2024 20:13:21 +0200 Subject: [PATCH 57/60] update tests --- .../gno.land/r/gnoland/events/events_test.gno | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 27b07e44ae1..cdb9d8b24c5 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -12,18 +12,15 @@ import ( var ( suRealm = std.NewUserRealm(su) - now = "2009-02-13T23:31:30Z" // time.Now() is hardcoded to this value in the gno test machine currently + + now = "2009-02-13T23:31:30Z" // time.Now() is hardcoded to this value in the gno test machine currently + parsedTimeNow, _ = time.Parse(time.RFC3339, now) ) func TestAddEvent(t *testing.T) { std.TestSetOrigCaller(su) std.TestSetRealm(suRealm) - parsedTimeNow, err := time.Parse(time.RFC3339, now) - if err != nil { - panic(err) - } - e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) e1End := e1Start.Add(time.Hour * 4) @@ -90,11 +87,6 @@ func TestAddEventErrors(t *testing.T) { func TestDeleteEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 - parsedTimeNow, err := time.Parse(time.RFC3339, now) - if err != nil { - panic(err) - } - e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) e1End := e1Start.Add(time.Hour * 4) @@ -117,46 +109,54 @@ func TestDeleteEvent(t *testing.T) { func TestEditEvent(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 - parsedTimeNow, err := time.Parse(time.RFC3339, now) - if err != nil { - panic(err) - } - e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) e1End := e1Start.Add(time.Hour * 4) + loc := "gnome land" - id, _ := AddEvent("ToDelete", "description", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + id, _ := AddEvent("ToDelete", "description", "gno.land", loc, e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) newName := "New Name" newDesc := "Normal description" - newST := int64(1234) - newET := int64(4567) + newLink := "new Link" + newST := e1Start.Add(time.Hour) + newET := newST.Add(time.Hour) + + EditEvent(seqid.ID(id), newName, newDesc, newLink, "", newST.Format(time.RFC3339), newET.Format(time.RFC3339)) + edited, _, _ := GetEventByID(seqid.ID(id)) + + // Check updated values + uassert.Equal(t, edited.name, newName) + uassert.Equal(t, edited.description, newDesc) + uassert.Equal(t, edited.link, newLink) + uassert.True(t, edited.startTime.Equal(newST)) + uassert.True(t, edited.endTime.Equal(newET)) + + // Check if the old values are the same + uassert.Equal(t, edited.location, loc) +} + +func TestInvalidEdit(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 - EditEvent(id, newName, newDesc, "", "", newST, newET) - edited, _, _ := GetEventByID(id) + uassert.PanicsWithMessage(t, ErrNoSuchID.Error(), func() { + EditEvent(seqid.ID(123123), "", "", "", "", "", "") + }) +} - if edited.name != newName { - t.Fatalf("Expected %s, got %s", newName, edited.name) - } +func TestParseTimes(t *testing.T) { + // times not provided + // end time before start time + // timezone Missmatch - if edited.description != newDesc { - t.Fatalf("Expected %s, got %s", newDesc, edited.description) - } + _, _, err := parseTimes("", "") + uassert.ErrorContains(t, err, ErrInvalidStartTime.Error()) - if edited.startTime != time.Unix(newST, 0) { - t.Fatalf("Expected %d, got %d", newST, edited.startTime.Unix()) - } + _, _, err = parseTimes(now, "") + uassert.ErrorContains(t, err, ErrInvalidEndTime.Error()) - if edited.endTime != time.Unix(newET, 0) { - t.Fatalf("Expected %d, got %d", newET, edited.endTime.Unix()) - } -} + _, _, err = parseTimes("2009-02-13T23:30:30Z", "2009-02-13T21:30:30Z") + uassert.ErrorContains(t, err, ErrEndBeforeStart.Error()) -//func TestInvalidEdit(t *testing.T) { -// events = nil // remove elements from previous tests - see issue #1982 -// id := AddEvent("ToEdit", "Desc to edit", "gno.land", "gnome land", 123, 456) -// -// uassert.PanicsWithMessage(t, ErrInvalidEndTime.Error(), func() { -// EditEvent(id, "", "", "", "", 0, -5) -// }) -//} + _, _, err = parseTimes("2009-02-10T23:30:30+02:00", "2009-02-13T21:30:33+05:00") + uassert.ErrorContains(t, err, ErrStartEndTimezonemMismatch.Error()) +} From 647c9b2c61a1c80344e8d1db125ff96c1698f3f8 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 10 Aug 2024 10:38:17 +0200 Subject: [PATCH 58/60] update doc --- examples/gno.land/r/gnoland/events/administration.gno | 2 ++ examples/gno.land/r/gnoland/events/events.gno | 4 ++++ examples/gno.land/r/gnoland/events/rendering.gno | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/gnoland/events/administration.gno b/examples/gno.land/r/gnoland/events/administration.gno index fc9944b44c1..02914adee69 100644 --- a/examples/gno.land/r/gnoland/events/administration.gno +++ b/examples/gno.land/r/gnoland/events/administration.gno @@ -11,10 +11,12 @@ var ( auth = authorizable.NewAuthorizableWithAddress(su) ) +// GetOwner gets the owner of the events realm func GetOwner() std.Address { return auth.Owner() } +// AddModerator adds a moderator to the events realm func AddModerator(mod std.Address) { auth.AssertCallerIsOwner() diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index fe383d123f2..f540905db24 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -1,3 +1,6 @@ +// Package events allows you to upload data about specific IRL/online events +// It includes dynamic support for updating rendering events based on their +// status, ie if they are upcoming, in progress, or in the past. package events import ( @@ -170,6 +173,7 @@ func (m eventsSlice) Swap(i, j int) { m[i], m[j] = m[j], m[i] } +// parseTimes parses the start and end time for an event and checks for possible errors func parseTimes(startTime, endTime string) (time.Time, time.Time, error) { st, err := time.Parse(time.RFC3339, startTime) if err != nil { diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index 3f54da47780..6205e2dde69 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -11,7 +11,7 @@ const ( MaxWidgetSize = 5 ) -// RenderEventWidget shows up to eventsToRender of the latest events to auth caller +// RenderEventWidget shows up to eventsToRender of the latest events to a caller func RenderEventWidget(eventsToRender int) (string, error) { numOfEvents := len(events) if numOfEvents == 0 { @@ -39,6 +39,7 @@ func RenderEventWidget(eventsToRender int) (string, error) { return output, nil } +// renderHome renders the home page of the events realm func renderHome(admin bool) string { output := "# gno.land events\n\n" @@ -101,6 +102,7 @@ func renderHome(admin bool) string { return output } +// Render returns the markdown representation of a single event instance func (e Event) Render(admin bool) string { var buf bytes.Buffer @@ -133,6 +135,7 @@ func (e Event) Render(admin bool) string { return buf.String() } +// Render is the main rendering entry point func Render(path string) string { if path == "admin" { return renderHome(true) From 04b0049444589f8a51c461fa4fa38e22de7d88f1 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sun, 11 Aug 2024 14:25:40 +0200 Subject: [PATCH 59/60] update types, update golden tests --- examples/gno.land/r/gnoland/home/home.gno | 2 +- examples/gno.land/r/gnoland/home/home_filetest.gno | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 31ac910534a..e5f24f87135 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -85,7 +85,7 @@ func lastContributions(limit int) ui.Element { func upcomingEvents() ui.Element { out, _ := events.RenderEventWidget(events.MaxWidgetSize) return ui.Element{ - ui.H3("[Upcoming Events](/r/gnoland/events)"), + ui.H3("[Latest Events](/r/gnoland/events)"), ui.Text(out), } } diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 17477401fbb..15f329683f4 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -74,7 +74,7 @@ func main() { //
//
// -// ### [Upcoming Events](/r/gnoland/events) +// ### [Latest Events](/r/gnoland/events) // // No events. //
From 335848bce0f0dedde0ac7c4cc182c1902e745e82 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sun, 11 Aug 2024 14:43:57 +0200 Subject: [PATCH 60/60] update tests/rendering --- examples/gno.land/r/gnoland/events/events.gno | 26 +++++++++---------- .../gno.land/r/gnoland/events/events_test.gno | 9 +++---- .../gno.land/r/gnoland/events/rendering.gno | 6 ++--- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index f540905db24..0984edf75a9 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -15,7 +15,7 @@ import ( type ( Event struct { - id seqid.ID + id string name string // name of event description string // short description of event link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page @@ -41,24 +41,24 @@ const ( // AddEvent adds auth new event // Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00 -func AddEvent(name, description, link, location, startTime, endTime string) (int64, error) { +func AddEvent(name, description, link, location, startTime, endTime string) (string, error) { auth.AssertOnAuthList() if strings.TrimSpace(name) == "" { - return -1, ErrEmptyName + return "", ErrEmptyName } if len(description) > maxDescLength { - return -1, ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) + return "", ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) } // Parse times st, et, err := parseTimes(startTime, endTime) if err != nil { - return -1, err + return "", err } - id := idCounter.Next() + id := idCounter.Next().String() e := &Event{ id: id, name: name, @@ -74,14 +74,14 @@ func AddEvent(name, description, link, location, startTime, endTime string) (int std.Emit(EventAdded, "id", - e.id.String(), + e.id, ) - return int64(id), nil + return id, nil } // DeleteEvent deletes an event with auth given ID -func DeleteEvent(id seqid.ID) { +func DeleteEvent(id string) { auth.AssertOnAuthList() e, idx, err := GetEventByID(id) @@ -93,14 +93,14 @@ func DeleteEvent(id seqid.ID) { std.Emit(EventDeleted, "id", - e.id.String(), + e.id, ) } // EditEvent edits an event with auth given ID // It only updates values corresponding to non-empty arguments sent with the call // Note: if you need to update the start time or end time, you need to provide both every time -func EditEvent(id seqid.ID, name, description, link, location, startTime, endTime string) { +func EditEvent(id string, name, description, link, location, startTime, endTime string) { auth.AssertOnAuthList() e, _, err := GetEventByID(id) @@ -143,11 +143,11 @@ func EditEvent(id seqid.ID, name, description, link, location, startTime, endTim std.Emit(EventEdited, "id", - e.id.String(), + e.id, ) } -func GetEventByID(id seqid.ID) (*Event, int, error) { +func GetEventByID(id string) (*Event, int, error) { for i, event := range events { if event.id == id { return event, i, nil diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index cdb9d8b24c5..1e5625e0b2c 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -6,7 +6,6 @@ import ( "testing" "time" - "gno.land/p/demo/seqid" "gno.land/p/demo/uassert" ) @@ -98,7 +97,7 @@ func TestDeleteEvent(t *testing.T) { t.Fatalf("Expected to find ToDelete event in render") } - DeleteEvent(seqid.ID(id)) + DeleteEvent(id) got = renderHome(false) if strings.Contains(got, "ToDelete") { @@ -121,8 +120,8 @@ func TestEditEvent(t *testing.T) { newST := e1Start.Add(time.Hour) newET := newST.Add(time.Hour) - EditEvent(seqid.ID(id), newName, newDesc, newLink, "", newST.Format(time.RFC3339), newET.Format(time.RFC3339)) - edited, _, _ := GetEventByID(seqid.ID(id)) + EditEvent(id, newName, newDesc, newLink, "", newST.Format(time.RFC3339), newET.Format(time.RFC3339)) + edited, _, _ := GetEventByID(id) // Check updated values uassert.Equal(t, edited.name, newName) @@ -139,7 +138,7 @@ func TestInvalidEdit(t *testing.T) { events = nil // remove elements from previous tests - see issue #1982 uassert.PanicsWithMessage(t, ErrNoSuchID.Error(), func() { - EditEvent(seqid.ID(123123), "", "", "", "", "", "") + EditEvent("123123", "", "", "", "", "", "") }) } diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno index 6205e2dde69..bde32065d27 100644 --- a/examples/gno.land/r/gnoland/events/rendering.gno +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -118,8 +118,8 @@ func (e Event) Render(admin bool) string { sign = "+" } - buf.WriteString(ufmt.Sprintf("**Start time:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) - buf.WriteString(ufmt.Sprintf("**End time:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + buf.WriteString(ufmt.Sprintf("**Starts:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + buf.WriteString(ufmt.Sprintf("**Ends:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) if admin { buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) @@ -127,7 +127,7 @@ func (e Event) Render(admin bool) string { } if e.link != "" { - buf.WriteString(ufmt.Sprintf("[Website](%s)\n\n", e.link)) + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) } buf.WriteString("
")