Skip to content

Commit

Permalink
website: implement greentext in a less scuffed way
Browse files Browse the repository at this point in the history
News page now loads through htmx, has comments and has infinite
scroll on the archive page.
  • Loading branch information
Wessie committed Feb 27, 2024
1 parent 5faac11 commit 51eed31
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 39 deletions.
4 changes: 2 additions & 2 deletions radio.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,10 +786,10 @@ type NewsStorage interface {
Delete(NewsPostID) error
// List returns a list of news post starting at offset and returning up to
// limit amount of posts, chronologically sorted by creation date
List(limit int, offset int) (NewsList, error)
List(limit int64, offset int64) (NewsList, error)
// ListPublic returns the same thing as List but with deleted and private
// posts filtered out
ListPublic(limit int, offset int) (NewsList, error)
ListPublic(limit int64, offset int64) (NewsList, error)
// Comments returns all comments associated with the news post given
Comments(NewsPostID) ([]NewsComment, error)
}
Expand Down
4 changes: 2 additions & 2 deletions storage/mariadb/news.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (ns NewsStorage) Delete(id radio.NewsPostID) error {
}

// List implements radio.NewsStorage
func (ns NewsStorage) List(limit int, offset int) (radio.NewsList, error) {
func (ns NewsStorage) List(limit int64, offset int64) (radio.NewsList, error) {
const op errors.Op = "mariadb/NewsStorage.List"

var query = `
Expand Down Expand Up @@ -205,7 +205,7 @@ func (ns NewsStorage) List(limit int, offset int) (radio.NewsList, error) {
}

// ListPublic implements radio.NewsStorage
func (ns NewsStorage) ListPublic(limit int, offset int) (radio.NewsList, error) {
func (ns NewsStorage) ListPublic(limit int64, offset int64) (radio.NewsList, error) {
const op errors.Op = "mariadb/NewsStorage.ListPublic"

var query = `
Expand Down
2 changes: 1 addition & 1 deletion templates/default/news-single.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<div class="columns">
<div class="column is-8 m-0 p-0">
{{range $comment := .}}
<div class="column block has-background-white-ter mb-4">
<div id="{{.ID}}" class="column block has-background-white-ter mb-4">
<div class="columns">
<div class="column is-narrow is-flex-mobile pb-0 ml-4">{{with .User}}{{.Username}}{{else}}Anonymous ({{.Identifier}}){{end}} #{{.ID}}</div>
<div class="column is-hidden-mobile"></div>
Expand Down
18 changes: 15 additions & 3 deletions templates/default/news.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,22 @@
<div class="title has-text-centered mb-6 is-hidden-mobile">
<h3>News Archive</h3>
</div>
<div class="columns is-multiline">
{{range $news := $.News.Entries}}
<div id="news-content" hx-boost="true" hx-push-url="true" hx-target="#content" class="columns is-multiline">
{{range $news := $.News}}
{{template "news-post" $news}}
{{end}}
{{with .Page.Next 1}}
<div id="news-scroll"
hx-push-url="false"
hx-trigger="intersect once"
hx-get="{{.URL}}"
hx-select="#news-content>div"
hx-target="#news-content"
hx-swap="beforeend"></div>
{{end}}
</div>
<div class="is-hidden-with-js">
{{template "pagination" .Page}}
</div>
</div>
</section>
Expand All @@ -25,7 +37,7 @@
<span class="has-text-left"></span>
<time datetime="{{.CreatedAt.Unix}}" data-type="absolute">{{.CreatedAt | AbsoluteDate}}</time>
</div>
<div class="message-body pt-0 disable-message-border">{{.HeaderParsed}}</div>
<div class="message-body pt-0 disable-message-border">{{.Header}}</div>
</article>
</a>
</div>
Expand Down
55 changes: 49 additions & 6 deletions website/public/news.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,78 @@ import (
"time"

radio "github.com/R-a-dio/valkyrie"
"github.com/R-a-dio/valkyrie/errors"
"github.com/R-a-dio/valkyrie/website/middleware"
"github.com/R-a-dio/valkyrie/website/shared"
"github.com/go-chi/chi/v5"
)

const newsPageSize = 20

type NewsInput struct {
middleware.Input

News radio.NewsList
News []NewsInputPost
NewsTotal int
Page *shared.Pagination
}

type NewsInputPost struct {
ID radio.NewsPostID
Title string
Header template.HTML
User radio.User

CreatedAt time.Time
UpdatedAt *time.Time
}

func (NewsInput) TemplateBundle() string {
return "news"
}

func NewNewsInput(s radio.NewsStorageService, r *http.Request) (*NewsInput, error) {
entries, err := s.News(r.Context()).ListPublic(20, 0)
func NewNewsInput(cache *shared.NewsCache, ns radio.NewsStorageService, r *http.Request) (*NewsInput, error) {
const op errors.Op = "website/public.NewNewsInput"

page, offset, err := shared.PageAndOffset(r, newsPageSize)
if err != nil {
return nil, errors.E(op, err)
}

entries, err := ns.News(r.Context()).ListPublic(newsPageSize, offset)
if err != nil {
return nil, err
}

posts := make([]NewsInputPost, 0, len(entries.Entries))
for _, post := range entries.Entries {
md, err := cache.RenderHeader(post)
if err != nil {
return nil, err
}

posts = append(posts, NewsInputPost{
ID: post.ID,
Title: post.Title,
Header: md.Output,
User: post.User,
CreatedAt: post.CreatedAt,
UpdatedAt: post.UpdatedAt,
})
}

return &NewsInput{
Input: middleware.InputFromRequest(r),
News: entries,
Input: middleware.InputFromRequest(r),
News: posts,
NewsTotal: entries.Total,
Page: shared.NewPagination(page, shared.PageCount(int64(entries.Total), newsPageSize),
r.URL,
),
}, nil
}

func (s State) GetNews(w http.ResponseWriter, r *http.Request) {
input, err := NewNewsInput(s.Storage, r)
input, err := NewNewsInput(s.News, s.Storage, r)
if err != nil {
s.errorHandler(w, r, err)
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
package shared
package markdown

import (
"bytes"
"fmt"
"reflect"
"strconv"
"unicode"

"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
goldutil "github.com/yuin/goldmark/util"
)

func RadioMarkdownOptions() []goldmark.Option {
return []goldmark.Option{
goldmark.WithParser(NoBlockQuoteParser()),
goldmark.WithParserOptions(
parser.WithInlineParsers(
goldutil.Prioritized(&MemeQuoteParser{}, 1),
),
),
goldmark.WithRendererOptions(
html.WithHardWraps(),
renderer.WithNodeRenderers(
goldutil.Prioritized(&MemeQuoteRenderer{}, 1),
),
),
}
}

func NoBlockQuoteParser() parser.Parser {
var found bool
adjustedBlockParsers := parser.DefaultBlockParsers()
Expand Down Expand Up @@ -85,10 +108,60 @@ func (p *MemeQuoteParser) Parse(parent ast.Node, reader text.Reader, pc parser.C
return link
}

if bytes.HasPrefix(line, greenMeme) {
parent.SetAttributeString("class", []byte("green-text"))
return nil
// TODO: implement this correctly
if bytes.HasPrefix(line, greenMeme) && reader.LineOffset() == 0 {
// if there is a
stop := len(line)
if tmp := bytes.IndexByte(line, '\n'); tmp > 0 {
// don't cut off the newline if it exists
stop = tmp
}

seg = text.NewSegment(seg.Start, seg.Start+stop)

green := &Node{}
green.AppendChild(green, ast.NewTextSegment(seg))
reader.Advance(stop)
return green
}

return nil
}

type MemeQuoteRenderer struct{}

var Kind = ast.NewNodeKind("greentext")

type Node struct {
ast.BaseInline
}

func (Node) Kind() ast.NodeKind {
return Kind
}

func (n *Node) Dump(src []byte, level int) {
ast.DumpHelper(n, src, level, nil, nil)
}

var _ ast.Node = (*Node)(nil)

func (r *MemeQuoteRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(Kind, r.Render)
}

func (r *MemeQuoteRenderer) Render(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
_, ok := node.(*Node)
if !ok {
return ast.WalkStop, fmt.Errorf("unexpected node %T, expected 'mememark.Node'", node)
}

if entering {
_, _ = w.WriteString(`<span class="green-text">`)
return ast.WalkContinue, nil
}

_, _ = w.WriteString(`</span>`)

return ast.WalkContinue, nil
}
35 changes: 35 additions & 0 deletions website/shared/markdown/greentext_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package markdown

import (
"bytes"
"fmt"
"testing"

"github.com/yuin/goldmark"
"github.com/yuin/goldmark/util"
)

var data []byte

func TestMemeQuotesExtension(t *testing.T) {
data = []byte(` `)

md := goldmark.New(RadioMarkdownOptions()...)

var buf bytes.Buffer
err := md.Convert([]byte(data), &buf)

fmt.Println(err)
fmt.Println(buf.String())
}

func FuzzRadioStyleMarkdown(f *testing.F) {
f.Fuzz(func(t *testing.T, orig string) {
markdown := goldmark.New(RadioMarkdownOptions()...)
var buf bytes.Buffer
err := markdown.Convert(util.StringToReadOnlyBytes(orig), &buf)
if err != nil {
panic(err)
}
})
}
29 changes: 8 additions & 21 deletions website/shared/markdown.go → website/shared/news.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,21 @@ import (
radio "github.com/R-a-dio/valkyrie"
"github.com/R-a-dio/valkyrie/util"
"github.com/R-a-dio/valkyrie/util/pool"
"github.com/R-a-dio/valkyrie/website/shared/markdown"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
goldutil "github.com/yuin/goldmark/util"
)

func NewNewsCache() *NewsCache {
return &NewsCache{
trusted: goldmark.New(
goldmark.WithParser(NoBlockQuoteParser()),
goldmark.WithParserOptions(
parser.WithInlineParsers(
goldutil.Prioritized(&MemeQuoteParser{}, 1),
),
),
goldmark.WithRendererOptions(
//html.WithUnsafe(), // TODO: see if we want to enable this
),
markdown.RadioMarkdownOptions()...,
//goldmark.WithRendererOptions(
//html.WithUnsafe(), // TODO: see if we want to enable this
//),
),
untrusted: goldmark.New(
goldmark.WithParser(NoBlockQuoteParser()),
goldmark.WithParserOptions(
parser.WithInlineParsers(
goldutil.Prioritized(&MemeQuoteParser{}, 1),
),
),
),
pool: pool.NewResetPool(func() *bytes.Buffer { return new(bytes.Buffer) }),
cache: new(util.Map[newsCacheKey, NewsMarkdown]),
untrusted: goldmark.New(markdown.RadioMarkdownOptions()...),
pool: pool.NewResetPool(func() *bytes.Buffer { return new(bytes.Buffer) }),
cache: new(util.Map[newsCacheKey, NewsMarkdown]),
}
}

Expand Down
File renamed without changes.

0 comments on commit 51eed31

Please sign in to comment.