diff --git a/api/routes/show.go b/api/routes/show.go
index f0c1e70..694a680 100644
--- a/api/routes/show.go
+++ b/api/routes/show.go
@@ -1,13 +1,12 @@
package routes
import (
- "fmt"
- "log"
- "strings"
+ "html/template"
"time"
"github.com/gofiber/fiber/v2"
"github.com/medium.rip/pkg/client"
+ "github.com/medium.rip/pkg/converters"
)
func show(c *fiber.Ctx) error {
@@ -23,23 +22,15 @@ func show(c *fiber.Ctx) error {
post := e.Data.Post
publishDate := time.UnixMilli(e.Data.Post.CreatedAt)
- log.Println(publishDate)
- var sb strings.Builder
-
- for _, node := range post.Content.BodyModel.Paragraphs {
- switch node.Type {
- case "H3":
- sb.WriteString(fmt.Sprintf("
%s
", node.Text))
- }
- }
+ p := converters.ConvertParagraphs(post.Content.BodyModel.Paragraphs)
return c.Render("show", fiber.Map {
"Title": post.Title,
"UserId": post.Creator.ID,
"Author": post.Creator.Name,
"PublishDate": publishDate.Format(time.DateOnly),
- "Nodes": post.Content.BodyModel.Paragraphs,
+ "Paragraphs": template.HTML(p),
})
}
diff --git a/frontend/dist/index.html b/frontend/dist/index.html
index b737067..9e661d4 100644
--- a/frontend/dist/index.html
+++ b/frontend/dist/index.html
@@ -6,7 +6,7 @@
{{ .Title }}
-
+
Hello, World!
diff --git a/frontend/dist/show.html b/frontend/dist/show.html
index fa61c49..291653d 100644
--- a/frontend/dist/show.html
+++ b/frontend/dist/show.html
@@ -14,21 +14,14 @@
rel="stylesheet">
{{ .Title }}
-
+
-
+
{{.Title}}
{{.Author}} on {{.PublishDate}}
- {{range .Nodes}}
- {{if eq .Type "H3"}}
- {{.Text}}
- {{end}}
- {{if eq .Type "IMG"}}
-
- {{end}}
- {{end}}
+ {{.Paragraphs}}
diff --git a/frontend/show.html b/frontend/show.html
index c417323..246aee7 100644
--- a/frontend/show.html
+++ b/frontend/show.html
@@ -17,17 +17,10 @@
-
+
{{.Title}}
{{.Author}} on {{.PublishDate}}
- {{range .Nodes}}
- {{if eq .type "H3"}}
- .text
- {{end}}
- {{if eq .type "IMG"}}
- .text
- {{end}}
- {{end}}
+ {{.Paragraphs}}
diff --git a/pkg/converters/markup_converter.go b/pkg/converters/markup_converter.go
index 88665f2..38ff7b5 100644
--- a/pkg/converters/markup_converter.go
+++ b/pkg/converters/markup_converter.go
@@ -9,20 +9,20 @@ import (
)
type RangeWithMarkup struct {
- Range []int
+ Range []int
Markups []entities.Markup
}
func unique(intSlice []int) []int {
- keys := make(map[int]bool)
- list := []int{}
- for _, entry := range intSlice {
- if _, value := keys[entry]; !value {
- keys[entry] = true
- list = append(list, entry)
- }
- }
- return list
+ keys := make(map[int]bool)
+ list := []int{}
+ for _, entry := range intSlice {
+ if _, value := keys[entry]; !value {
+ keys[entry] = true
+ list = append(list, entry)
+ }
+ }
+ return list
}
func ranges(text string, markups []entities.Markup) []RangeWithMarkup {
@@ -54,14 +54,14 @@ func ranges(text string, markups []entities.Markup) []RangeWithMarkup {
// check if this markup is covered by the range
coveredMarkups := make([]entities.Markup, 0)
for _, m := range markups {
- if (int(m.Start) >= start && int(m.Start) < end) || (int(m.End - 1) >= start && int(m.End - 1) < end) {
+ if (int(m.Start) >= start && int(m.Start) < end) || (int(m.End-1) >= start && int(m.End-1) < end) {
coveredMarkups = append(coveredMarkups, m)
}
}
// append the range
ranges = append(ranges, RangeWithMarkup{
- Range: []int{start, end},
+ Range: []int{start, end},
Markups: coveredMarkups,
})
}
@@ -69,22 +69,22 @@ func ranges(text string, markups []entities.Markup) []RangeWithMarkup {
return ranges
}
-func Convert(text string, markups []entities.Markup) string {
+func ConvertMarkup(text string, markups []entities.Markup) string {
var markedUp strings.Builder
for _, r := range ranges(text, markups) {
textToWrap := string(text[r.Range[0]:r.Range[1]])
- markedUp.WriteString(WrapInMarkups(textToWrap, r.Markups))
+ markedUp.WriteString(wrapInMarkups(textToWrap, r.Markups))
}
return markedUp.String()
}
-func WrapInMarkups(child string, markups []entities.Markup) string {
+func wrapInMarkups(child string, markups []entities.Markup) string {
if len(markups) == 0 {
return child
}
markedUp := markupNodeInContainer(child, markups[0])
- return WrapInMarkups(markedUp, markups[1:])
+ return wrapInMarkups(markedUp, markups[1:])
}
func markupNodeInContainer(child string, markup entities.Markup) string {
@@ -105,4 +105,4 @@ func markupNodeInContainer(child string, markup entities.Markup) string {
return fmt.Sprintf(`%s
`, child)
}
return child
-}
\ No newline at end of file
+}
diff --git a/pkg/converters/markup_converter_test.go b/pkg/converters/markup_converter_test.go
index 69e26d5..3e0f5ec 100644
--- a/pkg/converters/markup_converter_test.go
+++ b/pkg/converters/markup_converter_test.go
@@ -58,7 +58,7 @@ func TestRanges(t *testing.T) {
}
func TestConvert(t *testing.T) {
- markup := Convert("strong and emphasized only", []entities.Markup{
+ markup := ConvertMarkup("strong and emphasized only", []entities.Markup{
{
Type: "STRONG",
Start: 0,
@@ -74,4 +74,4 @@ func TestConvert(t *testing.T) {
if markup != "strong and emphasized only" {
t.Errorf("Expected markup to be strong and emphasized only, got %s", markup)
}
-}
\ No newline at end of file
+}
diff --git a/pkg/converters/paragraph_converter.go b/pkg/converters/paragraph_converter.go
new file mode 100644
index 0000000..7661e9a
--- /dev/null
+++ b/pkg/converters/paragraph_converter.go
@@ -0,0 +1,144 @@
+package converters
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/medium.rip/pkg/entities"
+)
+
+const IMAGE_HOST = "https://cdn-images-1.medium.com/fit/c"
+const MAX_WIDTH = 800
+const FALLBACK_HEIGHT = 600
+
+type Image struct {
+ ID string
+ OriginalHeight int64
+ OriginalWidth int64
+}
+
+func (i *Image) Initialize(originalWidth *int64, originalHeight *int64) {
+ if originalWidth != nil {
+ i.OriginalWidth = *originalWidth
+ } else {
+ i.OriginalWidth = MAX_WIDTH
+ }
+
+ if originalHeight != nil {
+ i.OriginalHeight = *originalHeight
+ } else {
+ i.OriginalHeight = FALLBACK_HEIGHT
+ }
+}
+
+func (i *Image) width() int64 {
+ if i.OriginalWidth > MAX_WIDTH {
+ return MAX_WIDTH
+ } else {
+ return i.OriginalWidth
+ }
+}
+
+func (i *Image) src() string {
+ return fmt.Sprintf("%s/%d/%d/%s", IMAGE_HOST, i.width(), i.height(), i.ID)
+}
+
+func (i *Image) height() int64 {
+ if i.OriginalWidth > MAX_WIDTH {
+ return i.OriginalHeight * int64(i.ratio())
+ } else {
+ return i.OriginalHeight
+ }
+}
+
+func (i *Image) ratio() float32 {
+ return float32(MAX_WIDTH) / float32(i.OriginalWidth)
+}
+
+func ConvertParagraphs(paragraphs []entities.Paragraph) string {
+ if len(paragraphs) == 0 {
+ return ""
+ }
+
+ var ps strings.Builder
+
+ for i, p := range paragraphs {
+ switch p.Type {
+ case "BQ", "MIXTAPE_EMBED", "PQ":
+ children := ConvertMarkup(p.Text, p.Markups)
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ case "H2":
+ children := ConvertMarkup(p.Text, p.Markups)
+ if p.Name != "" {
+ ps.WriteString(fmt.Sprintf("%s
", p.Name, children))
+ } else {
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ }
+ case "H3":
+ children := ConvertMarkup(p.Text, p.Markups)
+ if p.Name != "" {
+ ps.WriteString(fmt.Sprintf("%s
", p.Name, children))
+ } else {
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ }
+ case "H4":
+ children := ConvertMarkup(p.Text, p.Markups)
+ if p.Name != "" {
+ ps.WriteString(fmt.Sprintf("%s
", p.Name, children))
+ } else {
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ }
+ // TODO: handle IFRAME
+ case "IMG":
+ ps.WriteString(convertImg(p))
+ case "OLI":
+ listItems := convertOli(paragraphs[i:])
+ ps.WriteString(fmt.Sprintf("%s
", listItems))
+ case "ULI":
+ listItems := convertUli(paragraphs[i:])
+ ps.WriteString(fmt.Sprintf("", listItems))
+ case "P":
+ children := ConvertMarkup(p.Text, p.Markups)
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ case "PRE":
+ children := ConvertMarkup(p.Text, p.Markups)
+ ps.WriteString(fmt.Sprintf("%s
", children))
+ case "SECTION_CAPTION":
+ // unused
+ default:
+ }
+ }
+
+ return ps.String()
+}
+
+func convertImg(p entities.Paragraph) string {
+ if p.Metadata != nil {
+ captionMarkup := ConvertMarkup(p.Text, p.Markups)
+ img := Image{ID : p.Metadata.ID}
+ img.Initialize(&p.Metadata.OriginalWidth, &p.Metadata.OriginalHeight)
+ return fmt.Sprintf("", img.src(), img.width(), captionMarkup)
+ } else {
+ return ""
+ }
+}
+
+func convertOli(ps []entities.Paragraph) string {
+ if len(ps) != 0 && ps[0].Type == "OLI" {
+ p := ps[0]
+ children := ConvertMarkup(p.Text, p.Markups)
+ return fmt.Sprintf("%s", children) + convertOli(ps[1:])
+ } else {
+ return ""
+ }
+}
+
+func convertUli(ps []entities.Paragraph) string {
+ if len(ps) != 0 && ps[0].Type == "ULI" {
+ p := ps[0]
+ children := ConvertMarkup(p.Text, p.Markups)
+ return fmt.Sprintf("%s", children) + convertUli(ps[1:])
+ } else {
+ return ""
+ }
+}
diff --git a/pkg/converters/paragraph_converter_test.go b/pkg/converters/paragraph_converter_test.go
new file mode 100644
index 0000000..edc46a1
--- /dev/null
+++ b/pkg/converters/paragraph_converter_test.go
@@ -0,0 +1,22 @@
+package converters
+
+import (
+ "testing"
+
+ "github.com/medium.rip/pkg/entities"
+)
+
+func TestConvertOli(t *testing.T) {
+ oli := entities.Paragraph{
+ Name: "1-1",
+ Type: "OLI",
+ Text: "This is an ordered list item.",
+ Markups: []entities.Markup{},
+ }
+ olis := []entities.Paragraph{oli}
+ oliHTML := ConvertParagraphs(olis)
+ expected := "- This is an ordered list item.
"
+ if oliHTML != expected {
+ t.Errorf("ConvertParagraphs(olis) = %s; want %s", oliHTML, expected)
+ }
+}
\ No newline at end of file