diff --git a/examples/gno.land/p/demo/boardsv2/draft0/app.gno b/examples/gno.land/p/demo/boardsv2/draft0/app.gno deleted file mode 100644 index 28017f895c8..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/app.gno +++ /dev/null @@ -1,64 +0,0 @@ -package boardsv2 - -import ( - "gno.land/demo/p/boardsv2/post" - contentplugin "gno.land/demo/p/boardsv2/post/plugins/content" -) - -// type Rating struct{} -// -// var ratingIndex = avl.Tree{} -// app.AddBoardHook(func (changeType int, change ChangeSet) { -// if changeType == 0 { -// ratingIndex.Set("...", ) -// } -// }) - -type App struct { - st Storage - boards []Board -} - -func New(s Storage, o ...Option) App { - a := App{ - st: Storage, - } - // Define the rule for a spesific view. - boardsView := view.New(view.Filter{ - Level: 0, // this will give me the list of the boards. - }) - - return a -} - -func (a *App) AddBoard(name, title, description string) (*Board, error) { - p := post.New(contentplugin.TitleBasedContent{ - Title: title, - Description: description, - }) - - // I want to create a query for listing threads under this new board. - threadView := view.New(view.Filter{ - Level: 1, - SlugPrefix: name, - }) - userActivityView := view.New(view.Filter{ - LevelGte: 2, - By: func(content Content) []View { - c.Author // by account address - } - }) - - if err := post.Add(a.st, name, p); err != nil { - nil, err - } - return a.GetBoard(name), nil -} - -func (a *App) GetBoard(name string) (board *Board, found bool) { - -} - -func (a *App) ListBoards() ([]*Board, error) { - -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/board.gno b/examples/gno.land/p/demo/boardsv2/draft0/board.gno deleted file mode 100644 index e56895a9b1d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/board.gno +++ /dev/null @@ -1,24 +0,0 @@ -package boardsv2 - -type Board struct { -} - -func (b *Board) AddPost() error { - -} - -func (b *Board) GetThread(id string) (post *Post, found bool) { - -} - -func (b *Board) ListThreads(id string) (post *Post, found bool) { - threadView.List() // there should be an iterator, pagination -} - -func (b *Board) Fork() error { - -} - -func (b *Board) Lock() error { - -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/boardsv2.gno b/examples/gno.land/p/demo/boardsv2/draft0/boardsv2.gno deleted file mode 100644 index 91c308f9576..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/boardsv2.gno +++ /dev/null @@ -1,7 +0,0 @@ -// boardsv2 is a reddit like abstraction around post/*. -// You might implement other abstractions around post/* to create -// different type of dApps. -// refer to the app.gno file to get started. -package boardsv2 - - diff --git a/examples/gno.land/p/demo/boardsv2/draft0/option.gno b/examples/gno.land/p/demo/boardsv2/draft0/option.gno deleted file mode 100644 index b8d7b65c643..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/option.gno +++ /dev/null @@ -1,13 +0,0 @@ -package boardsv2 - -type Option struct{} - -// LinearReputationPolicy allows upvoting or downvoting a post by one -// for each account. -func LinearReputationPolicy() Option {} - -// TokenBasedReputationPolicy allows upvoting or downvoting a post propotional -// to the specified tokens that an account holds. -func TokenBasedReputationPolicy() Option {} - -// TODO: make it configurable how many levels allowed diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/avlstorage/avlstorage.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/avlstorage/avlstorage.gno deleted file mode 100644 index c1f6c226c20..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/avlstorage/avlstorage.gno +++ /dev/null @@ -1 +0,0 @@ -package avlstorage \ No newline at end of file diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/content.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/content.gno deleted file mode 100644 index cbfd45c36fe..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/content.gno +++ /dev/null @@ -1,5 +0,0 @@ -package post - -type Content interface { - Render() string -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/plugin.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/plugin.gno deleted file mode 100644 index 54be1f50a86..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/plugin.gno +++ /dev/null @@ -1 +0,0 @@ -package post \ No newline at end of file diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/plugins/content/content.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/plugins/content/content.gno deleted file mode 100644 index 05043e7e172..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/plugins/content/content.gno +++ /dev/null @@ -1,30 +0,0 @@ -package commentplugin - -type CommentContent struct { - Body string -} - -func (c CommentContent) Render() string { - -} - -type TitleBasedContent struct{} - -type TextContent struct { - Title string - Body string - Tags []string -} - -func (c TextContent) Render() string { - -} - -type PollContent struct { - Question string - Options []string - Votes []struct { - Address std.Adress - Option string - } -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/post.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/post.gno deleted file mode 100644 index d103579d437..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/post.gno +++ /dev/null @@ -1,90 +0,0 @@ -package post - -import "time" - -/* -alicePost = &Post { content: "foo" } (0x001) -bobFork := &Post { Origial: alicePost (0x001) } - -//1. Check gc behavior in realm for forks - ---- -alicePost := &(*alicePost) (0x002) -alicePost.content = "new content" - -bobFork := &Post { Origial: uintptr(0x001) } ---- -type Post struct { - ID int - Level int -} - -package reddit - -// explore with plugins -// - boardsv2 -// - pkg/general -// - pkg/reddit -var ( - rating avl.Tree -) - -genericPost := Post{} -reddit.UpvotePost(genericPost.ID) -*/ - -// Blog example -// Home -// - post 1 (content: title, body, author, label, timestamp) -// - post 1.1 (body, author) (thread) -// - post 1.1.1 (comment to a thread but also a new thread) -// - post 1.1.1.1 -// - post 1.2 (thread) -// -// - post 2 -// - post 3 -// -// Reddit example -// Home -// - post 1 (title, body) (board) -// - post 1.1 (title, body) (sub-board) -// - post 1.1.1 (title, body, label) -// - post 1.1.1.1 (comment, thread) -type Post struct { - ID string - Content Content // title, body, label, author, other metadata... - Level int - Base *Post - Children []*Post - Forks []*Post - UpdatedAt time.Time - CreatedAt time.Time // the time when created by user or forked. - Creator std.Address -} - -// create plugins for Post type < -// upvoting < implement first plugin -// define public API for plugin, post packages and boardsv2 -// moderation -// -// plugin ideas: -// - visibility -// - upcoting -// - acess control > you shouldn't be able to answer to the boards yo're not invited -// - moedaration (ban certain posts -this could be through a dao in the future) - -func New(s Storage) Post { - -} - -func Create(c Content) *Post { - -} - -func (p *Post) NextIncrementalKey(base string) string { - -} - -// func (p *Post) Append() error { -// -// } diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/storage.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/storage.gno deleted file mode 100644 index 49a5f7eef32..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/storage.gno +++ /dev/null @@ -1,4 +0,0 @@ -package post - -type Storage interface { -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/post/view.gno b/examples/gno.land/p/demo/boardsv2/draft0/post/view.gno deleted file mode 100644 index 3921e441039..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/post/view.gno +++ /dev/null @@ -1,10 +0,0 @@ -package post - -// Two cases to solve -// - Give me a list of boards (board list page) -// - Give me a list of comments, created by a user accross all boards (user activity page, of a user) -type View interface { - Name() string - Size() int - Iterate(start, end string, fn func(key string, v interface{}) bool) bool -} diff --git a/examples/gno.land/p/demo/boardsv2/draft0/thread.gno b/examples/gno.land/p/demo/boardsv2/draft0/thread.gno deleted file mode 100644 index 0ecad4ae41d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft0/thread.gno +++ /dev/null @@ -1,30 +0,0 @@ -package boardsv2 - -import ( - "gno.land/demo/p/boardsv2/post" - replyplugin "gno.land/demo/p/boardsv2/post/plugins/content/reply" -) - -type Thread struct { - post post.Post - st Store -} - -func (p *Thread) Comment(creator std.Address, message string) (id string, err error) { - pp := p.New(replyplugin.MessageContent{ - Message: message, - }) - id := p.post.NextIncrementalKey(creator.String()) // Post.ID/address/1 = "comment ID" - if err := post.Add(p.st, id); err != nil { - return "", err - } - return id, nil -} - -func (p *Thread) Upvote() error { - -} - -func (p *Thread) Downvote() error { - -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/boards.gno b/examples/gno.land/p/demo/boardsv2/draft1/boards.gno deleted file mode 100644 index f05972c0879..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/boards.gno +++ /dev/null @@ -1,69 +0,0 @@ -package boardsv2 - -import ( - "strconv" -) - -// TODO: Locking -// - Locking a board means, you can not create new threads, and you can not comment in existing ones -// - Locking a thread means, you can not comment in this thread anymore - -// TODO: Move boards (or App) to `boardsv2` -type Boards struct { - // NOTE: We might want different AVL trees to avoid using level prefixes - posts avl.Tree // string(Post.Level + Post.CreatedAt + slug) -> *Post (post, comment, poll) - locking lockingplugin.Plugin -} - -// TODO: Support pagination Start/End (see pager implementation) -func (b Boards) Iterate(level int, path string, fn func(*Post) bool) bool {} -func (b Boards) ReverseIterate(level int, path string, fn func(*Post) bool) bool {} - -func (b *Boards) Lock(path string) { - post := b.Get(LevelBoard, path) // Otherwise we try LevelPost - if err := b.locking.Lock(post); err != nil { - panic(err) - } -} - -// How to map render paths to actual post instances? -// -// AVL KEYS BY LEVEL PREFIX (start/end) -// Boards => 0_ ... 1_ -// Posts => 1_BOARD/ ... 2_ -// Comments => 2_BOARD/POST/ ... 3_ -// -// HOW TO GUESS PREFIX FROM SLUG -// User enters a SLUG => (one part => 1_BOARD)(more than one part => 1_BOARD/POST) -// How to recognize comments? Should be URL accesible? We could use ":" as separator (not optimal) -// -// LEVEL_BOARD/POST/POST-2/COMMENT/COMMENT-2 (deprecated) -// LEVEL_TIMESTAMP_BOARD/POST/COMMENT -// -// :board/post/comment - -func (b *Boards) Set(p *Post) (updated bool) { - key := newKey(p.Level, p.Slug()) - return b.posts.Set(key, p) -} - -func (b *Boards) Remove(level int, path string) (_ *Post, removed bool) { - key := newKey(level, path) - if v, removed := b.posts.Remove(key); removed { - return v.(*Post), true - } - return nil, false -} - -func (b Boards) Get(level int, path string) (_ *Post, found bool) { - key := newKey(level, path) - if v, found := b.posts.Get(key); found { - return v.(*Post), true - } - return "", false -} - -func newKey(level int, path string) string { - // TODO: Add timestamp to key - return strconv.Itoa(level) + "_" + path -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/content_board.gno b/examples/gno.land/p/demo/boardsv2/draft1/content_board.gno deleted file mode 100644 index e59af9bc4cf..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/content_board.gno +++ /dev/null @@ -1,30 +0,0 @@ -package boardsv2 - -// TODO: Move content types to `boardsv2` API - -const ContentTypeBoard = "boards:board" - -var _ Content = (*BoardContent)(nil) - -type BoardContent struct { - Name string - Tags []string -} - -func NewBoard() *Post { - return &Post{ // TODO: Use a contructor to be able to use private fields (use options), NewPost - // ... - Level: LevelBoard, - Content: &BoardContent{ - // ... - }, - } -} - -func (c BoardContent) Type() string { - return ContentTypeBoard -} - -func (c BoardContent) Render() string { - return "" -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/content_comment.gno b/examples/gno.land/p/demo/boardsv2/draft1/content_comment.gno deleted file mode 100644 index fcaac13c3e8..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/content_comment.gno +++ /dev/null @@ -1,27 +0,0 @@ -package boardsv2 - -const ContentTypeComment = "boards:comment" - -var _ Content = (*CommentContent)(nil) - -type CommentContent struct { - Body string -} - -func NewComment() *Post { - return &Post{ - // ... - Level: LevelComment, - Content: &CommentContent{ - // ... - }, - } -} - -func (c CommentContent) Type() string { - return ContentTypeComment -} - -func (c CommentContent) Render() string { - return "" -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/content_poll.gno b/examples/gno.land/p/demo/boardsv2/draft1/content_poll.gno deleted file mode 100644 index 1f3a0bc9846..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/content_poll.gno +++ /dev/null @@ -1,33 +0,0 @@ -package boardsv2 - -const ContentTypePoll = "boards:poll" - -var _ Content = (*PollContent)(nil) - -type PollContent struct { - Question string - Options []string - Votes []struct { - Address std.Adress - Option string - } - Tags []string -} - -func NewPoll( /* ... */ ) *Post { - return &Post{ - // ... - Level: LevelPost, - Content: &PollContent{ - // ... - }, - } -} - -func (c PollContent) Type() string { - return ContentTypePoll -} - -func (c PollContent) Render() string { - return "" -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/content_post.gno b/examples/gno.land/p/demo/boardsv2/draft1/content_post.gno deleted file mode 100644 index 289b5ba0099..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/content_post.gno +++ /dev/null @@ -1,29 +0,0 @@ -package boardsv2 - -const ContentTypePost = "boards:post" - -var _ Content = (*TextContent)(nil) - -type TextContent struct { - Title string - Body string - Tags []string -} - -func NewPost() *Post { - return &Post{ - // ... - Level: LevelPost, - Content: &TextContent{ - // ... - }, - } -} - -func (c TextContent) Type() string { - return ContentTypePost -} - -func (c TextContent) Render() string { - return "" -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/features.gno b/examples/gno.land/p/demo/boardsv2/draft1/features.gno deleted file mode 100644 index c5dff81eee4..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/features.gno +++ /dev/null @@ -1,69 +0,0 @@ -package boardsv2 - -import "errors" - -func AddBoard(s PostStore, slug string /* ... */) (path string, _ error) { - // TODO: Finish implementation - - return slug, nil -} - -// NOTE: Define a pattern to add functionality to posts by type (AddComment, AddThread, AddPoll, Repost, Upvote, ...) -// NOTE: Maybe though functions that assert the right arguments -func AddComment(s PostStore, parentPath string, creator std.Address, message string) (path string, _ error) { - // Try to get parent as a post or a comment, otherwise parent doesn't support comments - p, found := s.Get(LevelPost, parentPath) - if !found { - p, found = s.Get(LevelComment, parentPath) - if !found { - return "", errors.New("parent post or comment not found: " + parentPath) - } - } - - // TODO: - // Call the IsLocked function from the plugin for both the board post and thread post - // of this new comment. And confirm that both of them are false - // if so, then proceed, otherwise can not add new comments because locked. - // level 0 - boards - // level 1 - thread - // level 2 - comment - // level 3 - comment under comment - // level 4 - comment under comment under comment - // ... - - // TODO: - // Consider using reverse iteration while checking IsLocked in parent levels. - // If the keys in the AVL tree has levels as the prefix it should be optimized. If - // timestamp is used it may not be. - - comment := NewComment(p /* ... */) - - // TODO: Finish implementation - s.Set( /* ... */ ) - - path = parentPath + "/" + comment.ID - return path, nil -} - -// NOTE: Arguments could potentially be many, consider variadic + sane defaults (?) -func AddThread(s PostStore, parentPath, slug string, creator std.Address /* ... */) (path string, _ error) { - p, found := b.Get(LevelPost, parentPath) - if !found { - return "", errors.New("parent post not found: " + parentPath) - } - - post := NewPost(p, slug /* ... */) - - // TODO: Finish implementation - - path = parentPath + "/" + post.ID - return path, nil -} - -// ----- Other features ----- -// type VotesStore interface { -// /*...*/ -// } -// -// func Upvote(s VotesStore /* ... */) {} -// func DownVote(s VotesStore /* ... */) {} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/plugin/locking/locking.gno b/examples/gno.land/p/demo/boardsv2/draft1/plugin/locking/locking.gno deleted file mode 100644 index ba23251a4ef..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/plugin/locking/locking.gno +++ /dev/null @@ -1,50 +0,0 @@ -package lockingplugin - -import "errors" - -const Name = "boards:locking" - -var ErrInvalidPostType = errors.New("post type is not a board or thread") - -type ( - Plugin struct{} - Storage struct { - IsLocked bool - } -) - -func New() Plugin { - return Plugin{} -} - -func (p Plugin) Name() string { - return Name -} - -func (p *Plugin) Lock(p *Post) error { - if !isBoardOrThread(p) { - return ErrInvalidPostType - } - - p.MustGetPluginStorage(p.Name()).(*Storage).IsLocked = true -} - -func (p *Plugin) Unlock(p *Post) error { - if !isBoardOrThread(p) { - return ErrInvalidPostType - } - - p.MustGetPluginStorage(p.Name()).(*Storage).IsLocked = false -} - -func (p Plugin) IsLocked(p *Post) bool { - if !isBoardOrThread(p) { - return ErrInvalidPostType - } - - return p.MustGetPluginStorage(p.Name()).(*Storage).IsLocked -} - -func isBoardOrThread(p *Post) bool { - return p.Level == LevelBoard || p.Level == LevelPost -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/options.gno b/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/options.gno deleted file mode 100644 index 83287a53580..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/options.gno +++ /dev/null @@ -1,15 +0,0 @@ -package reputationplugin - -type Option func(*Plugin) - -func UseTokenBasePolicy() Option { - return func(p *Plugin) { - p.Policy = PolicyTokenBase - } -} - -func AllowedPostLevels(levels []int) Option { - return func(p *Plugin) { - p.AllowedPostLevels = levels - } -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/reputation.gno b/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/reputation.gno deleted file mode 100644 index 13fcfa3facd..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/plugin/reputation/reputation.gno +++ /dev/null @@ -1,77 +0,0 @@ -package reputationplugin - -import "errors" - -const ( - PolicyLinear = iota - PolicyTokenBase -) - -const Name = "boards:reputation" - -var ErrNotSupported = errors.New("reputation not supported") - -type ( - Plugin struct { - Policy int - AllowedPostLevels []int - } - - Storage struct { - Upvotes uint - Downbotes uint - ListOfWhoVotedWhat avl.Tree // string(std.Address) -> ?? (TODO: define) - } -) - -func NewReputationPlugin(o ...Option) Plugin { - var p Plugin - for _, apply := range o { - apply(&p) - } - return p -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) HasReputationSupport(p *Post) bool { - if len(p.AllowedPostLevels) == 0 { - return true - } - - for _, lvl := range p.AllowedPostLevels { - if p.Level == lvl { - return true - } - } - return false -} - -func (p *Plugin) Votes(p *Post) uint32 { - if !p.HasReputationSupport(p) { - return ErrNotSupported - } - - // TODO: Implement -} - -func (p *Plugin) Upvote(p *Post) error { - if !p.HasReputationSupport(p) { - return ErrNotSupported - } - - // TODO: Modify global state - // TODO: Modify local state - // TODO: Implement - st := p.MustGetPluginStorage(p.Name()).(*Storage) -} - -func (p *Plugin) Downvote(p *Post) error { - if !p.HasReputationSupport(p) { - return ErrNotSupported - } - - // TODO: Implement -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/post.gno b/examples/gno.land/p/demo/boardsv2/draft1/post.gno deleted file mode 100644 index 1498cda066f..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/post.gno +++ /dev/null @@ -1,45 +0,0 @@ -package boardsv2 - -import ( - "strconv" - "time" -) - -const ( - LevelBoard = iota - LevelPost - LevelComment -) - -type ( - Content interface { - Type() string - Render() string - } - - Post struct { - ID string - Content Content - PluginStorage avl.Tree // string(plugin name) -> interface{}(plugin storage) - Parent *Post - Level int - Base *Post - Children []*Post - Forks []*Post - UpdatedAt time.Time - CreatedAt time.Time - Creator std.Address - } -) - -func (p Post) MustGetPluginStorage(name string) interface{} { - if v, found := p.pluginStorage.Get(name); found { - return v - } - - panic("plugin storage not found: " + name) -} - -func (p Post) NextIncrementalKey(baseKey string) string { - return baseKey + "/" + strconv.Itoa(len(p.Children)) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft1/store.gno b/examples/gno.land/p/demo/boardsv2/draft1/store.gno deleted file mode 100644 index f8c98f3e724..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft1/store.gno +++ /dev/null @@ -1,9 +0,0 @@ -package boardsv2 - -// NOTE: Maybe we could abstract the location where posts are stored -type PostStore interface { - Set(*Post) (updated bool) - Get(level int, path string) (_ *Post, found bool) // NOTE: Level could be a type alias for better semantics - - // TODO: Add iterator (or define PostIterator interface) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/app.gno b/examples/gno.land/p/demo/boardsv2/draft2/app.gno deleted file mode 100644 index 30125302302..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/app.gno +++ /dev/null @@ -1,91 +0,0 @@ -package boards - -import ( - "plugin" - - "golang.org/x/mod/sumdb/storage" -) - -const ( - LevelBoard = iota - LevelThread - LevelComment -) - -type App struct { - cntx Context -} - -func New(st storage.PostStorage, o ...Option) App { - // TODO: avl.Tree feels wrong here but maybe get rid of the map anyway - p := map[plugin.Name]plugin.Plugin{ - plugintitle.Name: plugintitle.New(st), // content for boards - plugintext.Name: plugintext.New(st),// content for text based threads - pluginpoll.Name: pluginpoll.New(st),// content for poll based threads - plugincomment.Name: plugincomment.New(st),// content for comments to the threads - } - - c := Context{ - storage: st, - plugins: p, - } - - a := App{ - cntx: c, - } - - return a -} - -func (a App) Board(path string) (Board, error) { - a.c.Get(level, path func(){}) -} - -func (a App) Boards(c post.Cursor) ([]Board, error) { - -} - -func (a App) CreateBoard(c BoardContent) (Board, error) {} - -func (b App) Thread(path string) (Thread, error) { - return ThreadWithComments(path, nil) -} - -// Fork forks either a board or a thread by their path. -func (a App) Fork(path, newPath string) error {} - -// Lock locks either a board or a thread by their path. -// Once a board is locked new threads to the board and comments to the existing -// threads won't be allowed. -// Once a thread is locked new comments to the thread won't be allowed. -func (a App) Lock(path string) error {} - - -// ThreadWithComments returns a thread with its comments with the comment depth -// configured with commentDepth for direct and child comments. -// For ex. -// To get a thread with only 10 direct (parent level) comments use: -// - []int{10} -// To get a thread with 10 direct comments and 3 of their child comments use: -// - []int{10, 3} -// You can define configure this for more levels until you reach to value defined -// by MaxCommentDepth. -// By default the configuration is as follows: -// - []int{20, 3} -func (b App) ThreadWithComments(path string, commentDepth []int) (Thread, error) {} -func (b App) Threads(c post.Cursor) ([]Thread, error) {} -func (b App) CreateTextThread(c ThreadTextContent) (Thread, error) {} -func (b App) CreatePollThread(c ThreadPollContent) (Thread, error) {} - -// parentPath could be a path to thread (root), or path to any of the -// nested comments. -func (b App) Comments(parentPath string, c Cursor) ([]Comment, error) {} -func (b App) CreateComment(path string, c plugincomment.Content) (Comment, error) { - post, err := a.c.Plugin(plugincomment.Name).NewPost(c, LevelComment) - if err != nil { - return Comment{}, err - } - return Comment{Post: post, c: a.c} -} - -func (a App) Render(path string) string {} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/board.gno b/examples/gno.land/p/demo/boardsv2/draft2/board.gno deleted file mode 100644 index ac9258fa629..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/board.gno +++ /dev/null @@ -1,14 +0,0 @@ -package boards - -type Board struct { - post.Post - c Context -} - -func (b Board) Content() BoardContent { - return b.c.Plugin(pluginbasiccontent.Name).Content(b.Post) -} - -func (b Board) Render() string { - -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/comment.gno b/examples/gno.land/p/demo/boardsv2/draft2/comment.gno deleted file mode 100644 index d66a77d1a67..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/comment.gno +++ /dev/null @@ -1,8 +0,0 @@ -package boards - -type Comment struct { - post.Post - c Context -} - -func (c Comment) Content() CommentContent {} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/context.gno b/examples/gno.land/p/demo/boardsv2/draft2/context.gno deleted file mode 100644 index 15c31e1db1d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/context.gno +++ /dev/null @@ -1,38 +0,0 @@ -package boards - -import "plugin" - -type Context struct { - opts []Option - st post.Storage - plugs map[post.PluginName]plugin.Plugin -} - -func newContext() Context { - -} - -func (c Context) Plugin(n post.PluginName) post.Plugin { - -} - -func (c Context) Set(p *Post) (updated bool) { - key := newKey(p.Level, p.Slug()) - return b.posts.Set(key, p) -} - -func (c Context) Remove(level int, path string) (_ *Post, removed bool) { - key := newKey(level, path) - if v, removed := b.posts.Remove(key); removed { - return v.(*Post), true - } - return nil, false -} - -func (c Context) Get(level int, path string, iterator func()) (_ *Post, found bool) { - key := newKey(level, path) - if v, found := b.posts.Get(key); found { - return v.(*Post), true - } - return "", false -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/option.gno b/examples/gno.land/p/demo/boardsv2/draft2/option.gno deleted file mode 100644 index 48ab0cc1bff..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/option.gno +++ /dev/null @@ -1,19 +0,0 @@ -package boards - -type Option struct{} - -// LinearReputationPolicy allows upvoting or downvoting a post by one -// for each account. -func LinearReputationPolicy() Option {} - -// TokenBasedReputationPolicy allows upvoting or downvoting a post propotional -// to the specified tokens that an account holds. -func TokenBasedReputationPolicy() Option {} - -// MaxPostDepth configures the max depth for nested comments. -// 0 -> boards -// 1 -> threads -// 2 -> comments-1 (direct comments to the threads) -// The above are already reserved. -// Setting it to zero will disable comments. -func MaxCommentDepth(d int) Option {} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/cursor.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/cursor.gno deleted file mode 100644 index 44f8ebe85f6..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/cursor.gno +++ /dev/null @@ -1,8 +0,0 @@ -package post - -type Cursor struct { - FromID string - Count int -} - -func NewCursor(fromID string, count int) Cursor {} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/comment/comment.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/comment/comment.gno deleted file mode 100644 index 2c4d7a53994..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/comment/comment.gno +++ /dev/null @@ -1,34 +0,0 @@ -package plugincomment - -const Name = "post-comment" - -func New(st Storage) Plugin { -} - -type Plugin struct { - postStorage Storage -} - -type Content struct { // Content of the comment. - Title string - Description string - Tags []string -} - -func (p Plugin) CreateComment(id string, c Content, level int) *post.Post { - pp := &post.Post{ - ID: id, - Level: level, - } - p.EditCommentContent(pp, c) - return pp -} - -func (p Plugin) Content(pst *post.Post) Content { - return pst.PluginStorage[Name].(Content) -} - -func (p Plugin) EditCommentContent(pp *Post, c Content) (updated bool) { - pp.PluginStorage[Name] = c - return p.postStorage.Set(post.ID, pp) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/lock/lock.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/lock/lock.gno deleted file mode 100644 index 5cd488aaabe..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/lock/lock.gno +++ /dev/null @@ -1 +0,0 @@ -package pluginlock diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/plugin.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/plugin.gno deleted file mode 100644 index 2f1cff7e627..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/plugin.gno +++ /dev/null @@ -1,7 +0,0 @@ -package plugin - -type Plugin interface { - Type() string -} - -type Name string diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/poll/poll.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/poll/poll.gno deleted file mode 100644 index db9c0bb40ce..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/poll/poll.gno +++ /dev/null @@ -1 +0,0 @@ -package pluginpoll diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/reputation/reputation.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/reputation/reputation.gno deleted file mode 100644 index 155fc53850f..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/reputation/reputation.gno +++ /dev/null @@ -1 +0,0 @@ -package pluginreputation diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/text/text.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/text/text.gno deleted file mode 100644 index a9f41c39947..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/text/text.gno +++ /dev/null @@ -1,3 +0,0 @@ -package plugintext - -const Name = "post-text" diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/title/title.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/title/title.gno deleted file mode 100644 index da2f8e905b5..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/plugin/title/title.gno +++ /dev/null @@ -1,24 +0,0 @@ -package plugintitle - -const Name = "post-title-only" - -func New(st Storage) Plugin { -} - -type Plugin struct { - st Storage -} - -type Content struct { - Title string - Description string - Tags []string -} - -func (p Plugin) Content(post *Post) Content { - return post.Body[Name].(Content) -} - -func (p Plugin) SetContent(post *Post, c Content) { - post.Body[Name] = c -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/post.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/post.gno deleted file mode 100644 index 04b6b88f48c..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/post.gno +++ /dev/null @@ -1,26 +0,0 @@ -package boardsv2 - -import ( - "plugin" - "strconv" - "time" -) - -type ( - Post struct { - ID string - PluginStore plugin.Plugin - Parent *Post - Level int - Base *Post - Children []*Post - Forks []*Post - UpdatedAt time.Time - CreatedAt time.Time - Creator std.Address - } -) - -func (p Post) NextIncrementalKey(baseKey string) string { - return baseKey + "/" + strconv.Itoa(len(p.Children)) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft2/post/store/store.gno b/examples/gno.land/p/demo/boardsv2/draft2/post/store/store.gno deleted file mode 100644 index 72440ea2a61..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/post/store/store.gno +++ /dev/null @@ -1 +0,0 @@ -package store diff --git a/examples/gno.land/p/demo/boardsv2/draft2/thread.gno b/examples/gno.land/p/demo/boardsv2/draft2/thread.gno deleted file mode 100644 index 42e731d4fd2..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft2/thread.gno +++ /dev/null @@ -1,18 +0,0 @@ -package boards - -type Thread struct { - post.Post - c Context -} - -func (t Thread) TextContent() ThreadTextContent { - -} - -func (t Thread) PollContent() ThreadPollContent {} -func (t Thread) Type() ContentType {} - -// Comments returns a list of comments sent to the thread. -// The comment slice will be non-nil only when Thread is initiated -// through ThreadWithComments. -func (t Thread) Comments() []Comment {} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/app.gno b/examples/gno.land/p/demo/boardsv2/draft3/app.gno deleted file mode 100644 index 3893e0866f7..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/app.gno +++ /dev/null @@ -1,133 +0,0 @@ -package boards - -import ( - "gno.land/p/demo/boards/post" - "gno.land/p/demo/boards/post/plugin" - pluginfork "gno.land/p/demo/boards/post/plugin/fork" - pluginpoll "gno.land/p/demo/boards/post/plugin/poll" - pluginreputation "gno.land/p/demo/boards/post/plugin/reputation" - plugintitle "gno.land/p/demo/boards/post/plugin/title" -) - -const ( - LevelBoard = iota - LevelThread - LevelComment -) - -// App is the boards application. -type App struct { - posts post.Store - plugins *plugin.Registry - maxCommentsDepth int - reputationPolicy pluginreputation.Policy -} - -// New creates a new boards application. -func New(st post.Store, options ...Option) App { - app := App{ - posts: st, - maxCommentsDepth: -1, // Infinite number of comments - } - for _, apply := range options { - apply(&app) - } - - app.plugins := plugin.NewRegistry( - plugintitle.New(st), // Plugin for boards - plugintext.New(st), // Plugin for text based threads - pluginpoll.New(st), // Plugin for poll based threads - plugincomment.New(st), // Plugin for comments to the threads - pluginreputation.New( - pluginreputation.UsePolicy(app.reputationPolicy), - pluginreputation.AllowedPostLevels(post.LevelPost, post.LevelComment), - ), - pluginfork.New( - pluginfork.AllowedPostLevels(post.LevelPost), - ), - ) - return app -} - -func (a App) GetBoard(path string) (_ Board, found bool) { - p, found := a.posts.GetByLevel(path, LevelBoard) - if !found { - return Board{}, false - } - return Board{p}, true -} - -func (a App) GetThread(path string) (_ Thread, found bool) { - p, found := a.posts.GetByLevel(path, LevelThread) - if !found { - return Thread{}, false - } - return Thread{p}, true -} - -func (a App) GetComment(path string) (_ Comment, found bool) { - p, found := a.posts.GetByLevel(path, LevelComment) - if !found { - return Comment{}, false - } - return Comment{p}, true -} - -func (a App) CreateBoard(slug, title, description string, tags []string) (Board, error) {} - -// Fork forks either a board or a thread by their path. -func (a App) ForkBoard(b Board, newPath string) error { - // NOTE: Instead of `app.ForkBoard()` we could use `b.Fork(newPath)` instead but that requires Board to have plugin access - // NOTE: This case gets the plugin from the plugin list to fork - p, _ := a.plugins.Get(pluginfork.Name) - return p.Fork(b.Post, newPath) -} - -func (a App) ForkThread(t Thread, newPath string) error { - // TODO: Implement thread fork app support -} - -// Lock locks either a board or a thread by their path. -// Once a board is locked new threads to the board and comments to the existing -// threads won't be allowed. -// Once a thread is locked new comments to the thread won't be allowed. -func (a App) Lock(path string) error {} - -// ---- TODO: Review the following list of app methods ----- // - -func (a App) Boards(c post.Cursor) ([]Board, error) { -} - -func (b App) Thread(path string) (Thread, error) { - return ThreadWithComments(path, nil) -} - -// ThreadWithComments returns a thread with its comments with the comment depth -// configured with commentDepth for direct and child comments. -// For ex. -// To get a thread with only 10 direct (parent level) comments use: -// - []int{10} -// To get a thread with 10 direct comments and 3 of their child comments use: -// - []int{10, 3} -// You can define configure this for more levels until you reach to value defined -// by MaxCommentDepth. -// By default the configuration is as follows: -// - []int{20, 3} -func (b App) ThreadWithComments(path string, commentDepth []int) (Thread, error) {} -func (b App) Threads(c post.Cursor) ([]Thread, error) {} -func (b App) CreateTextThread(c ThreadTextContent) (Thread, error) {} -func (b App) CreatePollThread(c ThreadPollContent) (Thread, error) {} - -// parentPath could be a path to thread (root), or path to any of the -// nested comments. -func (b App) Comments(parentPath string, c Cursor) ([]Comment, error) {} - -func (b App) CreateComment(path string, c plugincomment.Content) (Comment, error) { - post, err := a.c.Plugin(plugincomment.Name).NewPost(c, LevelComment) - if err != nil { - return Comment{}, err - } - return Comment{Post: post, c: a.c} -} - -func (a App) Render(path string) string {} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/board.gno b/examples/gno.land/p/demo/boardsv2/draft3/board.gno deleted file mode 100644 index b75db758a79..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/board.gno +++ /dev/null @@ -1,52 +0,0 @@ -package boards - -import ( - "gno.land/p/demo/boards/post" - pluginfork "gno.land/p/demo/boards/post/plugin/fork" - pluginreputation "gno.land/p/demo/boards/post/plugin/reputation" - plugintitle "gno.land/p/demo/boards/post/plugin/title" -) - -type ( - BoardContent plugintitle.Content - - Board struct { - *post.Post - } -) - -func NewBoard(pst *post.Post) Board { - // TODO: Local plugins must be initialized here (same for other plugins) - return Board{pst} -} - -func (b Board) Info() BoardContent { - return BoardContent(b.getContent()) -} - -func (b Board) Update(c BoardContent) { - b.PluginStore[plugintitle.Name] = plugintitle.Content(c) -} - -func (b Board) Upvote() error { - r := b.getReputation() - return r.Upvote(b.Post) -} - -func (b Board) Downvote() error { - r := b.getReputation() - return r.Downvote(b.Post) -} - -func (b Board) Render() string { - c := b.getContent() - return c.Render() -} - -func (b Board) getContent() *plugintitle.Content { - return b.PluginStore[plugintitle.Name].(*plugintitle.Content) -} - -func (b Board) getReputation() *pluginreputation.Reputation { - return b.PluginStore[pluginreputation.Name].(*pluginreputation.Reputation) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/comment.gno b/examples/gno.land/p/demo/boardsv2/draft3/comment.gno deleted file mode 100644 index d66a77d1a67..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/comment.gno +++ /dev/null @@ -1,8 +0,0 @@ -package boards - -type Comment struct { - post.Post - c Context -} - -func (c Comment) Content() CommentContent {} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/context.gno b/examples/gno.land/p/demo/boardsv2/draft3/context.gno deleted file mode 100644 index f3139abccb4..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/context.gno +++ /dev/null @@ -1,36 +0,0 @@ -package boards - -import "plugin" - -type Context struct { - opts []Option - st post.Store - plugs map[post.PluginName]plugin.Plugin -} - -func newContext() Context { -} - -func (c Context) Plugin(n post.PluginName) post.Plugin { -} - -func (c Context) Set(p *Post) (updated bool) { - key := newKey(p.Level, p.Slug()) - return b.posts.Set(key, p) -} - -func (c Context) Remove(level int, path string) (_ *Post, removed bool) { - key := newKey(level, path) - if v, removed := b.posts.Remove(key); removed { - return v.(*Post), true - } - return nil, false -} - -func (c Context) Get(level int, path string, iterator func()) (_ *Post, found bool) { - key := newKey(level, path) - if v, found := b.posts.Get(key); found { - return v.(*Post), true - } - return "", false -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/options.gno b/examples/gno.land/p/demo/boardsv2/draft3/options.gno deleted file mode 100644 index 906b1130663..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/options.gno +++ /dev/null @@ -1,31 +0,0 @@ -package boards - -import ( - pluginreputation "gno.land/p/demo/boards/post/plugin/reputation" -) - -// Option configures board applications. -type Options func(*App) - -// LinearReputationPolicy allows upvoting or downvoting a post by one for each account. -func LinearReputationPolicy() Option { - return func(a *App) { - a.reputationPolicy = pluginreputation.PolicyLinear - } -} - -// TokenBasedReputationPolicy allows upvoting or downvoting -// a post propotional to the specified tokens that an account holds. -func TokenBasedReputationPolicy() Option { - return func(a *App) { - a.reputationPolicy = pluginreputation.PolicyTokenBased - } -} - -// MaxCommentsDepth configures the max depth for nested comments. -// Setting it to -1 allows an infinite number of nested comments (default). -func MaxCommentsDepth(d int) Option { - return func(a *App) { - a.maxCommentsDepth = d - } -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/comment/comment.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/comment/comment.gno deleted file mode 100644 index f7b1eed2e5e..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/comment/comment.gno +++ /dev/null @@ -1,50 +0,0 @@ -package plugincomment - -const Name = "post-comment" - -type ( - Plugin struct { - posts post.Store - } - - // Content is the comment's content. - Content struct { - Title string - Description string - Tags []string - } -) - -func New(st post.Store) Plugin { - return Plugin{ - posts: st, - } -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - // TODO: Implement render support for comments - return "" -} - -func (p Plugin) CreateComment(id string, c Content, level int) *post.Post { - pst := &post.Post{ - ID: id, - Level: level, - } - p.SetContent(pst, c) - return pst -} - -func (p Plugin) Content(pst *post.Post) (_ *Content, ok bool) { - c, ok := pst.PluginStore[Name].(*Content) - return c, ok -} - -func (p Plugin) SetContent(pst *post.Post, c Content) (updated bool) { - ps.PluginStore[Name] = c - return p.posts.Set(pst) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/fork.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/fork.gno deleted file mode 100644 index 543f18b6e8d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/fork.gno +++ /dev/null @@ -1,47 +0,0 @@ -package pluginfork - -import ( - "gno.land/p/demo/boards/post" -) - -const Name = "fork" - -// TODO: Implement fork plugin to support thread forking -type Plugin struct { - AllowedPostLevels []int -} - -func New(o ...Option) Plugin { - var p Plugin - for _, apply := range o { - apply(&p) - } - return p -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - // TODO: Implement render support for text - return "" -} - -func (p Plugin) HasForkSupport(pst *post.Post) bool { - if len(p.AllowedPostLevels) == 0 { - return true - } - - for _, lvl := range p.AllowedPostLevels { - if pst.Level == lvl { - return true - } - } - return false -} - -func (p Plugin) Fork(pst *post.Post, newPath string) error { - // TODO: Implement fork support - return nil -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/options.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/options.gno deleted file mode 100644 index 85fe888bcb1..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/fork/options.gno +++ /dev/null @@ -1,9 +0,0 @@ -package pluginfork - -type Option func(*Plugin) - -func AllowedPostLevels(levels []int) Option { - return func(p *Plugin) { - p.AllowedPostLevels = levels - } -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/lock/lock.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/lock/lock.gno deleted file mode 100644 index fe75dd364c5..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/lock/lock.gno +++ /dev/null @@ -1,55 +0,0 @@ -package pluginlock - -import "errors" - -const Name = "lock" - -var ErrInvalidPostType = errors.New("post type is not a board or thread") - -type ( - Plugin struct{} - Lock struct { - IsLocked bool - } -) - -func New() Plugin { - return Plugin{} -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - return "" -} - -func (p *Plugin) Lock(pst *post.Post) error { - if !isBoardOrThread(pst) { - return ErrInvalidPostType - } - - pst.PluginStore[Name].(*Lock).IsLocked = true -} - -func (p *Plugin) Unlock(pst *post.Post) error { - if !isBoardOrThread(pst) { - return ErrInvalidPostType - } - - pst.PluginStore[Name].(*Lock).IsLocked = false -} - -func (p Plugin) IsLocked(pst *post.Post) bool { - if !isBoardOrThread(pst) { - return ErrInvalidPostType - } - - // TODO: Check parents if current post is not locked - return pst.PluginStore[Name].(*Lock).IsLocked -} - -func isBoardOrThread(pst *post.Post) bool { - return pst.Level == post.LevelBoard || pst.Level == post.LevelPost -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/plugin.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/plugin.gno deleted file mode 100644 index 5a42dc8bd10..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/plugin.gno +++ /dev/null @@ -1,48 +0,0 @@ -// TODO: Document how plugins work and best practices -package plugin - -import ( - "gno.land/p/demo/avl" -) - -type ( - // NOTE: Consider adding lifecycle methods like `Post` creation, deletion, ... - Plugin interface { - Name() string - Render() string - } - - Registry struct { - plugins avl.Tree // string(name) -> Plugin - } -) - -func NewRegistry(plugins ...Plugin) *Registry { - r := &Registry{} - for _, p := range plugins { - r.plugins.Set(p.Name(), p) - } - return r -} - -func (r Registry) Has(name string) bool { - return r.posts.Has(name) -} - -func (r Registry) Get(name string) (_ Plugin, found bool) { - if v, found := r.plugins.Get(name); found { - return v.(Plugin), true - } - return nil, false -} - -func (r *Registry) Add(p Plugin) { - r.plugins.Set(p.Name(), p) -} - -func (r *Registry) Remove(name string) (_ Plugin, removed bool) { - if v, removed := r.plugins.Remove(name, p); removed { - return v.(Plugin), false - } - return nil, false -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/poll/poll.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/poll/poll.gno deleted file mode 100644 index a20a4a22dd7..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/poll/poll.gno +++ /dev/null @@ -1,47 +0,0 @@ -package pluginpoll - -const Name = "post-poll" - -type ( - Plugin struct { - posts post.Store - } - - Poll struct { - Question string - Options []string - Votes []struct { - Address std.Adress - Option string - } - Tags []string - } -) - -func New(st post.Store) Plugin { - return Plugin{ - posts: st, - } -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - return "" -} - -func (p Plugin) CreatePoll(id string, v Poll) *post.Post { - pst := &post.Post{ - ID: id, - Level: LevelPost, - } - p.SetPoll(pst, v) - return pst -} - -func (p Plugin) SetPoll(pst *post.Post, v Poll) (updated bool) { - pst.PluginStore[Name] = v - return p.posts.Set(pst.ID, pst) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/options.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/options.gno deleted file mode 100644 index a87b45bf1e0..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/options.gno +++ /dev/null @@ -1,15 +0,0 @@ -package pluginreputation - -type Option func(*Plugin) - -func UsePolicy(v Policy) Option { - return func(p *Plugin) { - p.Policy = v - } -} - -func AllowedPostLevels(levels []int) Option { - return func(p *Plugin) { - p.AllowedPostLevels = levels - } -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/reputation.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/reputation.gno deleted file mode 100644 index 0ec8e1406af..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/reputation.gno +++ /dev/null @@ -1,99 +0,0 @@ -package pluginreputation - -import ( - "errors" - "std" -) - -// NOTE: Think about implementing a reputation based policy -const ( - PolicyLinear Policy = iota - PolicyTokenBased -) - -const Name = "reputation" - -var ErrNotSupported = errors.New("reputation not supported") - -type ( - Policy int - - Plugin struct { - Store Store - Policy Policy - AllowedPostLevels []int - } - - Reputation struct { - Upvotes uint - Downvotes uint - } -) - -func New(o ...Option) Plugin { - var p Plugin - for _, apply := range o { - apply(&p) - } - return p -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - return "" -} - -func (p Plugin) HasReputationSupport(pst *post.Post) bool { - if len(p.AllowedPostLevels) == 0 { - return true - } - - for _, lvl := range p.AllowedPostLevels { - if pst.Level == lvl { - return true - } - } - return false -} - -func (p Plugin) Votes(pst *post.Post) (upvotes uint64, downvotes uint64) { - if !p.HasReputationSupport(pst) { - return ErrNotSupported - } - - r := pst.PluginStore[Name].(*Reputation) - return r.Upvotes, r.Downvotes -} - -func (p Plugin) Voters(pst *post.Post) []std.Address { - if !p.HasReputationSupport(pst) { - return ErrNotSupported - } - - // TODO: Implement support for tracking voters -} - -func (p *Plugin) Upvote(pst *post.Post) error { - if !p.HasReputationSupport(pst) { - return ErrNotSupported - } - - // TODO: Handle accounts and change downvotes for existing accounts that downvoted - r := pst.PluginStore[Name].(*Reputation) - r.Upvotes++ - p.store.inc(pst.ID) -} - -func (p *Plugin) Downvote(pst *post.Post) error { - if !p.HasReputationSupport(pst) { - return ErrNotSupported - } - - // TODO: Handle accounts and change upvotes for existing accounts that upvoted - r := pst.PluginStore[Name].(*Reputation) - r.Downvotes++ - p.store.dec(pst.ID) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/store.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/store.gno deleted file mode 100644 index 9661210f830..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/reputation/store.gno +++ /dev/null @@ -1,58 +0,0 @@ -package pluginreputation - -import ( - "gno.land/p/demo/seqid" -) - -type ( - VotesIterFn = func(votes uint64, path string) bool - - Store struct { - votes avl.Tree // string(count) -> string(path) - } -) - -func (s Store) Iterate(fn VotesIterFn) bool { - // TODO: Support pagination of votes? - return s.votes.Iterate("", "", func(key string, v interface{}) bool { - count, _ := seqid.FromBinary(key) - return fn(uint64(count), v.(string)) - }) -} - -func (s *Store) inc(path string) uint64 { - var ( - current seqid.ID - v, found = s.votes.Get(path) - ) - if found { - current = v.(seqid.ID) - // TODO: Implement the right solution because this is not right, just showcase - s.votes.Remove(current.Binary()) - } - - current.Next() - s.votes.Set(current.Binary(), path) - return uint64(current) -} - -func (s *Store) dec(path string) uint64 { - var ( - current seqid.ID - v, found = s.votes.Get(path) - ) - if found { - current = v.(seqid.ID) - } - - if current == 0 { - return current - } - - s.votes.Remove(current.Binary()) - current-- - if current != 0 { - s.votes.Set(current.Binary(), current) - } - return uint64(current) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/text/text.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/text/text.gno deleted file mode 100644 index 014956e367b..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/text/text.gno +++ /dev/null @@ -1,38 +0,0 @@ -// plugintext is a content type for representing a tweet, blog post or a thread like Reddit. -package plugintext - -import ( - "gno.land/p/demo/boards/post" // NOTE: Plugins should be at the same level of post package -) - -const Name = "post-text" - -type ( - Plugin struct{} - Content struct { - Title string - Body string - Tags []string - } -) - -func New() Plugin { - return Plugin{} -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - // TODO: Implement render support for text - return "" -} - -func (p Plugin) Content(pst *post.Post) Content { - return pst.Body[Name].(*Content) -} - -func (p Plugin) SetContent(pst *post.Post, c Content) { - pst.Body[Name] = c -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/title/title.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/title/title.gno deleted file mode 100644 index 506f32001a8..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/plugin/title/title.gno +++ /dev/null @@ -1,35 +0,0 @@ -// plugintext is a content type for representing organizations, categories or sections. -package plugintitle - -const Name = "post-title" - -type ( - Plugin struct{} - - Content struct { - Title string - Description string - Tags []string - } -) - -func New() Plugin { - return Plugin{} -} - -func (p Plugin) Name() string { - return Name -} - -func (p Plugin) Render() string { - // TODO: Implement render support for title - return "" -} - -func (p Plugin) Content(pst *post.Post) Content { - return pst.Body[Name].(*Content) -} - -func (p Plugin) SetContent(pst *post.Post, c Content) { - pst.Body[Name] = c -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/post.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/post.gno deleted file mode 100644 index 229a904fd66..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/post.gno +++ /dev/null @@ -1,25 +0,0 @@ -package post - -import ( - "strconv" - "time" - - "gno.land/p/demo/boards/post/plugin" -) - -type Post struct { - ID string - PluginStore plugin.Plugin - Parent *Post - Level int - Base *Post - Children []*Post - Forks []*Post - UpdatedAt time.Time - CreatedAt time.Time - Creator std.Address -} - -func (p Post) NextIncrementalKey(baseKey string) string { - return baseKey + "/" + strconv.Itoa(len(p.Children)) -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/post/store.gno b/examples/gno.land/p/demo/boardsv2/draft3/post/store.gno deleted file mode 100644 index 49513f250db..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/post/store.gno +++ /dev/null @@ -1,31 +0,0 @@ -package post - -func NewStore() Store { - return Store{} -} - -// TODO: Implement posts store -type Store struct { - posts avl.Tree // string(level + creation timestamp + slug) -> *Post - slugs avl.Tree // string(slug) -> *Post -} - -func (s Store) Get(path string) (_ *Post, found bool) { - if v, found := s.slugs.Get(path); found { - return v.(*Post), true - } - return nil, false -} - -func (s Store) GetByLevel(path string, level int) (_ *Post, found bool) { - v, found := s.slugs.Get(path) - if !found { - return nil, false - } - - p := v.(*Post) - if p.Level != level { - return nil, false - } - return p, true -} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/store/cursor.gno b/examples/gno.land/p/demo/boardsv2/draft3/store/cursor.gno deleted file mode 100644 index c4fc379c28d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/store/cursor.gno +++ /dev/null @@ -1,9 +0,0 @@ -package store - -// TODO: Define how cursors should be used alongside stores -type Cursor struct { - FromID string - Count int -} - -func NewCursor(fromID string, count int) Cursor {} diff --git a/examples/gno.land/p/demo/boardsv2/draft3/store/store.gno b/examples/gno.land/p/demo/boardsv2/draft3/store/store.gno deleted file mode 100644 index 5c7fba3db4d..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/store/store.gno +++ /dev/null @@ -1,6 +0,0 @@ -package store - -// TODO: Define a storage interface and create and avl.Tree wrapper -type Store interface{} - -type AVLTreeStore struct{} // TODO: Use IAVL instead if there is a package implemented diff --git a/examples/gno.land/p/demo/boardsv2/draft3/thread.gno b/examples/gno.land/p/demo/boardsv2/draft3/thread.gno deleted file mode 100644 index cce35799b6f..00000000000 --- a/examples/gno.land/p/demo/boardsv2/draft3/thread.gno +++ /dev/null @@ -1,64 +0,0 @@ -package boards - -import ( - "gno.land/p/demo/boards/post" - pluginfork "gno.land/p/demo/boards/post/plugin/fork" - pluginpoll "gno.land/p/demo/boards/post/plugin/poll" - pluginreputation "gno.land/p/demo/boards/post/plugin/reputation" - plugintext "gno.land/p/demo/boards/post/plugin/text" -) - -type ( - ThreadContent plugintext.Content - - // TODO: Should polls be handler within this type? - Thread struct { - *post.Post - } -) - -func (t Thread) Info() ThreadContent { - return ThreadContent(t.getContent()) -} - -func (t Thread) Update(c ThreadContent) { - t.PluginStore[plugintext.Name] = plugintext.Content(c) -} - -func (t Thread) Upvote() error { - r := t.getReputation() - return r.Upvote(t.Post) -} - -func (t Thread) Downvote() error { - r := t.getReputation() - return r.Downvote(t.Post) -} - -func (t Thread) Fork(newPath string) error { - f := t.getFork() - return f.Fork(t.Post) -} - -func (t Thread) Render() string { - c := t.getContent() - return c.Render() -} - -// Comments returns a list of comments sent to the thread. -// The comment slice will be non-nil only when Thread is initiated -// through ThreadWithComments. -// TODO: Add support to get sub-threads (any type) and comments -// func (t Thread) Comments() []Comment {} - -func (t Thread) getContent() *plugintext.Content { - return t.PluginStore[plugintext.Name].(*plugintext.Content) -} - -func (t Thread) getReputation() *pluginreputation.Reputation { - return t.PluginStore[pluginreputation.Name].(*pluginreputation.Reputation) -} - -func (t Thread) getFork() *pluginfork.Fork { - return t.PluginStore[pluginfork.Name].(*pluginfork.Fork) -} diff --git a/examples/gno.land/r/demo/boardsv2/boardsv2.gno b/examples/gno.land/r/demo/boardsv2/boardsv2.gno deleted file mode 100644 index 4205c210da5..00000000000 --- a/examples/gno.land/r/demo/boardsv2/boardsv2.gno +++ /dev/null @@ -1,44 +0,0 @@ -package boardsv2 - -import "gno.land/p/demo/avl" - -// TODO: This goes in the realm -// type Boards struct { -// // TODO: Define how do we want to display and sort boards and posts (upvotes, pinned, ...) -// boards avl.Tree -// Title string -// Description string -// } - -func Render(path string) string { - // TODO: Implement render - return "" -} - -// TODO: Define public API - -func CreateBoard() {} // Maybe -func EditBoard() {} // Maybe -func ForkBoard() {} // Maybe - -func CreatePost() {} -func EditPost() {} -func ForkPost() {} -func DeletePost() {} -func Repost() {} -func Pin() {} -func Invite() {} // Maybe: Could also rely on an allow list -func UpVote() {} -func DownVote() {} - -func Comment() {} // Maybe -func EditComment() {} // Maybe -func DeleteComment() {} // Maybe - -func ToggleCommentsSupport() {} // Maybe -func ToggleThreadsSupport() {} // Maybe -func GetTags() {} // Maybe: List of allowed tags (moderated) - -func AddModerator() {} // Maybe -func RemoveModerator() {} // Maybe -func GetModerators() {} // Maybe diff --git a/examples/gno.land/r/demo/boardsv2/draft2/main.gno b/examples/gno.land/r/demo/boardsv2/draft2/main.gno deleted file mode 100644 index 6f8d203d31a..00000000000 --- a/examples/gno.land/r/demo/boardsv2/draft2/main.gno +++ /dev/null @@ -1,15 +0,0 @@ -package boards - -var postStore = avl.Tree{} // string(level + timestamp + slug) -> *Post - -func newApp() boards.App { // stateless approach for App struct - return boards.New( - postStore, - boards.MaxCommentDepth(10), - boards.LinearReputationPolicy(), - ) -} - -func Boards(c post.Cursor) ([]boards.Board, error) { - return newApp().Boards(c) -} diff --git a/examples/gno.land/r/demo/boardsv2/draft3/boards.gno b/examples/gno.land/r/demo/boardsv2/draft3/boards.gno deleted file mode 100644 index efc316fa458..00000000000 --- a/examples/gno.land/r/demo/boardsv2/draft3/boards.gno +++ /dev/null @@ -1,68 +0,0 @@ -package boards - -import ( - "std" - - "gno.land/p/demo/boards" - "gno.land/p/demo/boards/post" -) - -var app = boards.New( - post.NewStore(), - boards.MaxCommentDepth(10), - boards.LinearReputationPolicy(), -) - -func Render(path string) string { - // TODO: Define how to render the tree of boards, posts and comments - return "" -} - -func CreateBoard(slug, title, description string, tags []string) (path string) { - creator := std.GetOrigCaller() - board := app.CreateBoard(slug, title, description, tags, creator) - return board.ID -} - -func Lock(path string) { - post := getBoardOrThread(path) - if post == nil { - panic("path doesn't exist or locking this path not supported") - } - - assertOrigCallerIsCreator(post) - - // NOTE: Explore if it's better to use Post or Board/Thread types - if err := app.Lock(post); err != nil { - panic(err) - } -} - -func Fork(path, newPath string) { - post := getBoardOrThread(path) - if post == nil { - panic("path doesn't exist or forking this path not supported") - } - - // TODO: Use this way - app.ForkBoard(board) - app.ForkThread(thread) - - if err := app.Fork(post, newPath); err != nil { - panic(err) - } -} - -func getBoardOrThread(path string) *post.Post { - p, found := app.GetPost(path) - if found && (p.Level == boards.LevelBoard || p.Level == boards.LevelThread) { - return p - } - return nil -} - -func assertOrigCallerIsCreator(p *post.Post) { - if post.Creator != std.GetOrigCaller() { - panic("original caller is not allowed to perform this action") - } -}