krz/skunky-art

Alternative privacy frontend for DeviantArt.

clone: git clone https://gitbay.org/krz/skunky-art.git

cbafaa54d79d73fd1cc56ecba540d4f763591100

unsigned

author: lost+skunk <skunky@macaw.me> · 2024-06-14T17:04:23Z

панель навигации, ответы и ежедневные арты
 .gitignore          |   3 ++
 app/router.go       |   2 +
 app/wraper.go       | 125 +++++++++++++++++++++++++++++++++++++++++++++++-----
 config.example.json |  12 +++++
 css/skunky.css      |   5 +++
 html/list.htm       |  21 +++++++++
 html/search.htm     |   1 -
 main.go             |   8 +++-
 8 files changed, 165 insertions(+), 12 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4686488
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+**/cache
+**/config.json
+**/skunkyart
diff --git a/app/router.go b/app/router.go
index b4af3f1..e53f3ce 100644
--- a/app/router.go
+++ b/app/router.go
@@ -60,6 +60,8 @@ func Router() {
 			skunky.Deviation(url[:slash], url[slash+1:])
 		case "search":
 			skunky.Search()
+		case "dd":
+			skunky.DD()
 		case "media":
 			skunky.Emojitar(url)
 		case "about":
diff --git a/app/wraper.go b/app/wraper.go
index d8a459f..e252530 100644
--- a/app/wraper.go
+++ b/app/wraper.go
@@ -47,7 +47,77 @@ func (s skunkyart) httperr(status int) {
 	wr(s.Writer, msg.String())
 }
 
-func (s skunkyart) DeviationList(devs []devianter.Deviation) string {
+// навигация по страницам
+type dlist struct {
+	Pages int
+	More  bool
+}
+
+// FIXME: на некоротрых артах первая страница может вызывать полное отсутствие панели навигации.
+func (s skunkyart) NavBase(c dlist) string {
+	// TODO: сделать понятнее
+	// навигация по страницам
+	var list strings.Builder
+	list.WriteString("<br>")
+	p := s.Page
+
+	// функция для генерации ссылок
+	prevrev := func(msg string, page int, onpage bool) {
+		if !onpage {
+			list.WriteString(`<a href="?p=`)
+			list.WriteString(strconv.Itoa(page))
+			if s.Type != 0 {
+				list.WriteString("&type=")
+				list.WriteRune(s.Type)
+			}
+			if s.Query != "" {
+				list.WriteString("&q=")
+				list.WriteString(s.Query)
+			}
+			list.WriteString(`">`)
+			list.WriteString(msg)
+			list.WriteString("</a> ")
+		} else {
+			list.WriteString(strconv.Itoa(page))
+			list.WriteString(" ")
+		}
+	}
+
+	// вперёд-назад
+	if p > 1 {
+		prevrev("<= Prev |", p-1, false)
+	} else {
+		p = 1
+	}
+
+	if c.Pages > 0 {
+		// назад
+		for x := p - 6; x < p && x > 0; x++ {
+			prevrev(strconv.Itoa(x), x, false)
+		}
+
+		// вперёд
+		for x := p; x <= p+6; x++ {
+			if x == p {
+				prevrev("", x, true)
+				x++
+			}
+
+			if x > p {
+				prevrev(strconv.Itoa(x), x, false)
+			}
+		}
+	}
+
+	// вперёд-назад
+	if c.More {
+		prevrev("| Next =>", p+1, false)
+	}
+
+	return list.String()
+}
+
+func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) string {
 	var list strings.Builder
 	list.WriteString(`<div class="content">`)
 	for _, data := range devs {
@@ -81,6 +151,8 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation) string {
 		list.WriteString("</a></div>")
 	}
 	list.WriteString("</div>")
+	list.WriteString(s.NavBase(content[0]))
+
 	return list.String()
 }
 
@@ -102,7 +174,6 @@ func (s skunkyart) Deviation(author, postname string) {
 		// время публикации
 		post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
 
-		println(post.Post.Description)
 		// хештэги
 		for _, x := range post.Post.Deviation.Extended.Tags {
 			var tag strings.Builder
@@ -115,34 +186,52 @@ func (s skunkyart) Deviation(author, postname string) {
 			post.Tags += tag.String()
 		}
 
+		// FIXME: первый комментарий не отображается.
 		// генерация комментов
 		var cmmts strings.Builder
-		var replied map[int]string
-		_ = replied
+		replied := make(map[int]string)
 		c := devianter.CommentsFunc(id, post.Post.Comments.Cursor, s.Page, 1)
 
 		cmmts.WriteString("<details><summary>Comments: <b>")
 		cmmts.WriteString(strconv.Itoa(c.Total))
 		cmmts.WriteString("</b></summary>")
 		for _, x := range c.Thread {
-			cmmts.WriteString(`<div class="msg"><p id="`)
+			replied[x.ID] = x.User.Username
+			cmmts.WriteString(`<div class="msg`)
+			if x.Parent > 0 {
+				cmmts.WriteString(` reply`)
+			}
+			cmmts.WriteString(`"><p id="`)
 			cmmts.WriteString(strconv.Itoa(x.ID))
 			cmmts.WriteString(`"><img src="/media/`)
 			cmmts.WriteString(x.User.Username)
 			cmmts.WriteString(`?type=a" width="30px" height="30px"><a href="/user/`)
 			cmmts.WriteString(x.User.Username)
 			cmmts.WriteString(`"><b`)
+			cmmts.WriteString(` class="`)
 			if x.User.Banned {
-				cmmts.WriteString(` class="banned"`)
+				cmmts.WriteString(`banned`)
 			}
 			if x.Author {
-				cmmts.WriteString(` class="author"`)
+				cmmts.WriteString(`author`)
 			}
-			cmmts.WriteString(">")
+			cmmts.WriteString(`">`)
 			cmmts.WriteString(x.User.Username)
 			cmmts.WriteString("</b></a> ")
+			if x.Parent > 0 {
+				cmmts.WriteString(` In reply to <a href="#`)
+				cmmts.WriteString(strconv.Itoa(x.Parent))
+				cmmts.WriteString(`">`)
+				if replied[x.Parent] == "" {
+					cmmts.WriteString("???")
+				} else {
+					cmmts.WriteString(replied[x.Parent])
+				}
+				cmmts.WriteString("</a>")
+			}
+			cmmts.WriteString(" [")
 			cmmts.WriteString(x.Posted.UTC().String())
-			cmmts.WriteString("<p>")
+			cmmts.WriteString("]<p>")
 			cmmts.WriteString(x.Comment)
 			cmmts.WriteString("<p>👍: ")
 			cmmts.WriteString(strconv.Itoa(x.Likes))
@@ -150,7 +239,12 @@ func (s skunkyart) Deviation(author, postname string) {
 			cmmts.WriteString(strconv.Itoa(x.Replies))
 			cmmts.WriteString("</p></div>\n")
 		}
+		cmmts.WriteString(s.NavBase(dlist{
+			Pages: 0,
+			More:  c.HasMore,
+		}))
 		cmmts.WriteString("</details>")
+
 		post.Comments = cmmts.String()
 
 		s.exe("html/deviantion.htm", &post)
@@ -159,6 +253,14 @@ func (s skunkyart) Deviation(author, postname string) {
 	}
 }
 
+func (s skunkyart) DD() {
+	dd := devianter.DailyDeviationsFunc(s.Page)
+	s.exe("html/list.htm", s.DeviationList(dd.Deviations, dlist{
+		Pages: 0,
+		More:  dd.HasMore,
+	}))
+}
+
 func (s skunkyart) Search() {
 	// тут всё и так понятно
 	if s.Type == 'a' || s.Type == 't' || s.Type == 'g' {
@@ -170,7 +272,10 @@ func (s skunkyart) Search() {
 		var e error
 		srch.Search, e = devianter.SearchFunc(s.Query, s.Page, s.Type)
 		err(e)
-		srch.List = s.DeviationList(srch.Search.Results)
+		srch.List = s.DeviationList(srch.Search.Results, dlist{
+			Pages: srch.Search.Pages,
+			More:  srch.Search.HasMore,
+		})
 
 		s.exe("html/search.htm", &srch)
 	} else {
diff --git a/config.example.json b/config.example.json
new file mode 100644
index 0000000..1d979bd
--- /dev/null
+++ b/config.example.json
@@ -0,0 +1,12 @@
+{
+    "listen": "0.0.0.0:3003",
+    "base_uri": null,
+    "cache": {
+        "enabled": true,
+        "path": "cache",
+        "lifetime": null,
+        "max_size": 10000
+    },
+    "proxy": false,
+    "nsfw": false
+}
\ No newline at end of file
diff --git a/css/skunky.css b/css/skunky.css
index ef281ce..4a4af0d 100644
--- a/css/skunky.css
+++ b/css/skunky.css
@@ -47,6 +47,11 @@ form input, button, select {
 .msg:hover {
     background-color: #134134;
 }
+.reply {
+    border-radius: 0px 2px 2px 0px;
+    border-left: #258268 solid;
+    margin-left: 40px;
+}
 .dd {
     color: rgb(160, 0, 147);
 }
diff --git a/html/list.htm b/html/list.htm
new file mode 100644
index 0000000..42bd2a5
--- /dev/null
+++ b/html/list.htm
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>SkunkyArt</title>
+        <link rel="stylesheet" href="gui/css/skunky.css">
+    </head>
+    <main>
+        <header>
+            <h1><a href="/">HOME</a> | <a href="/dd">DD</a></h1>
+            <form method="get" action="/search">
+                <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
+                <select name="type">
+                    <option value="all">All</option>
+                    <option value="tag">Tag</option>
+                </select>
+                <button type="submit">Search!</button>
+            </form>
+        </header>
+        {{.}}
+    </main>
+</html>
\ No newline at end of file
diff --git a/html/search.htm b/html/search.htm
index 4ef9fbd..8ab9438 100644
--- a/html/search.htm
+++ b/html/search.htm
@@ -21,7 +21,6 @@
         <h1>Total resuls: {{.Search.Total}}</h1>
         {{end}}
         {{.List}}
-        {{.Search.Pages}}
         {{else}}
         <p>No results :(</p>
         {{end}}
diff --git a/main.go b/main.go
index 412f4fa..eab427b 100644
--- a/main.go
+++ b/main.go
@@ -1,7 +1,13 @@
 package main
 
-import "skunkyart/app"
+import (
+	"skunkyart/app"
+
+	"git.macaw.me/skunky/devianter"
+)
 
 func main() {
+	devianter.UpdateCSRF()
+
 	app.Router()
 }