Skip to content

Commit

Permalink
feat(boardsv2): experiment API - WIP (#2902)
Browse files Browse the repository at this point in the history
Boards v2 package and realm API experimentation

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

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>

---------

Co-authored-by: İlker G. Öztürk <[email protected]>
Co-authored-by: Denys Sedchenko <[email protected]>
  • Loading branch information
3 people authored Nov 4, 2024
1 parent baf9821 commit 1a88d58
Show file tree
Hide file tree
Showing 66 changed files with 2,047 additions and 4 deletions.
1 change: 0 additions & 1 deletion examples/gno.land/p/demo/boardsv2/boardsv2.gno

This file was deleted.

64 changes: 64 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/app.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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) {

}
24 changes: 24 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/board.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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 {

}
7 changes: 7 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/boardsv2.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 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


13 changes: 13 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/option.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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
5 changes: 5 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/post/content.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package post

type Content interface {
Render() string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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
}
}
90 changes: 90 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/post/post.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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 {
//
// }
4 changes: 4 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/post/storage.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package post

type Storage interface {
}
10 changes: 10 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/post/view.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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
}
30 changes: 30 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft0/thread.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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 {

}
69 changes: 69 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft1/boards.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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
}
30 changes: 30 additions & 0 deletions examples/gno.land/p/demo/boardsv2/draft1/content_board.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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 ""
}
Loading

0 comments on commit 1a88d58

Please sign in to comment.