microblog.gno

2.46 Kb ยท 124 lines
  1package microblog
  2
  3import (
  4	"errors"
  5	"sort"
  6	"std"
  7	"strings"
  8	"time"
  9
 10	"gno.land/p/demo/avl"
 11	"gno.land/p/demo/ufmt"
 12)
 13
 14var (
 15	ErrNotFound    = errors.New("not found")
 16	StatusNotFound = "404"
 17)
 18
 19type Microblog struct {
 20	Title  string
 21	Prefix string   // i.e. r/gnoland/blog:
 22	Pages  avl.Tree // author (string) -> Page
 23}
 24
 25func NewMicroblog(title string, prefix string) (m *Microblog) {
 26	return &Microblog{
 27		Title:  title,
 28		Prefix: prefix,
 29		Pages:  avl.Tree{},
 30	}
 31}
 32
 33func (m *Microblog) GetPages() []*Page {
 34	var (
 35		pages = make([]*Page, m.Pages.Size())
 36		index = 0
 37	)
 38
 39	m.Pages.Iterate("", "", func(key string, value any) bool {
 40		pages[index] = value.(*Page)
 41		index++
 42		return false
 43	})
 44
 45	sort.Sort(byLastPosted(pages))
 46
 47	return pages
 48}
 49
 50func (m *Microblog) NewPost(text string) error {
 51	author := std.OriginCaller()
 52	_, found := m.Pages.Get(author.String())
 53	if !found {
 54		// make a new page for the new author
 55		m.Pages.Set(author.String(), &Page{
 56			Author:    author,
 57			CreatedAt: time.Now(),
 58		})
 59	}
 60
 61	page, err := m.GetPage(author.String())
 62	if err != nil {
 63		return err
 64	}
 65	return page.NewPost(text)
 66}
 67
 68func (m *Microblog) GetPage(author string) (*Page, error) {
 69	silo, found := m.Pages.Get(author)
 70	if !found {
 71		return nil, ErrNotFound
 72	}
 73	return silo.(*Page), nil
 74}
 75
 76type Page struct {
 77	ID         int
 78	Author     std.Address
 79	CreatedAt  time.Time
 80	LastPosted time.Time
 81	Posts      avl.Tree // time -> Post
 82}
 83
 84// byLastPosted implements sort.Interface for []Page based on
 85// the LastPosted field.
 86type byLastPosted []*Page
 87
 88func (a byLastPosted) Len() int           { return len(a) }
 89func (a byLastPosted) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 90func (a byLastPosted) Less(i, j int) bool { return a[i].LastPosted.After(a[j].LastPosted) }
 91
 92func (p *Page) NewPost(text string) error {
 93	now := time.Now()
 94	p.LastPosted = now
 95	p.Posts.Set(ufmt.Sprintf("%s%d", now.Format(time.RFC3339), p.Posts.Size()), &Post{
 96		ID:        p.Posts.Size(),
 97		Text:      text,
 98		CreatedAt: now,
 99	})
100	return nil
101}
102
103func (p *Page) GetPosts() []*Post {
104	posts := make([]*Post, p.Posts.Size())
105	i := 0
106	p.Posts.ReverseIterate("", "", func(key string, value any) bool {
107		postParsed := value.(*Post)
108		posts[i] = postParsed
109		i++
110		return false
111	})
112	return posts
113}
114
115// Post lists the specific update
116type Post struct {
117	ID        int
118	CreatedAt time.Time
119	Text      string
120}
121
122func (p *Post) String() string {
123	return "> " + strings.ReplaceAll(p.Text, "\n", "\n>\n>") + "\n>\n> *" + p.CreatedAt.Format(time.RFC1123) + "*"
124}