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}