Commit cbafaa54d7

cbafaa54d79d73fd1cc56ecba540d4f763591100

parent: 6f872fe2f7

Unsigned

lost+skunk <skunky@macaw.me> · 2024-06-14 17:04 UTC

панель навигации, ответы и ежедневные арты

Layout: unified · split

.gitignore added +3
@@ -0,0 +1,3 @@
1**/cache
2**/config.json
3**/skunkyart
app/router.go +2
@@ -60,6 +60,8 @@ func Router() {
60 skunky.Deviation(url[:slash], url[slash+1:]) 60 skunky.Deviation(url[:slash], url[slash+1:])
61 case "search": 61 case "search":
62 skunky.Search() 62 skunky.Search()
63 case "dd":
64 skunky.DD()
63 case "media": 65 case "media":
64 skunky.Emojitar(url) 66 skunky.Emojitar(url)
65 case "about": 67 case "about":
app/wraper.go +115 −10
@@ -47,7 +47,77 @@ func (s skunkyart) httperr(status int) {
47 wr(s.Writer, msg.String()) 47 wr(s.Writer, msg.String())
48} 48}
49 49
50func (s skunkyart) DeviationList(devs []devianter.Deviation) string { 50// навигация по страницам
51type dlist struct {
52 Pages int
53 More bool
54}
55
56// FIXME: на некоротрых артах первая страница может вызывать полное отсутствие панели навигации.
57func (s skunkyart) NavBase(c dlist) string {
58 // TODO: сделать понятнее
59 // навигация по страницам
60 var list strings.Builder
61 list.WriteString("<br>")
62 p := s.Page
63
64 // функция для генерации ссылок
65 prevrev := func(msg string, page int, onpage bool) {
66 if !onpage {
67 list.WriteString(`<a href="?p=`)
68 list.WriteString(strconv.Itoa(page))
69 if s.Type != 0 {
70 list.WriteString("&type=")
71 list.WriteRune(s.Type)
72 }
73 if s.Query != "" {
74 list.WriteString("&q=")
75 list.WriteString(s.Query)
76 }
77 list.WriteString(`">`)
78 list.WriteString(msg)
79 list.WriteString("</a> ")
80 } else {
81 list.WriteString(strconv.Itoa(page))
82 list.WriteString(" ")
83 }
84 }
85
86 // вперёд-назад
87 if p > 1 {
88 prevrev("<= Prev |", p-1, false)
89 } else {
90 p = 1
91 }
92
93 if c.Pages > 0 {
94 // назад
95 for x := p - 6; x < p && x > 0; x++ {
96 prevrev(strconv.Itoa(x), x, false)
97 }
98
99 // вперёд
100 for x := p; x <= p+6; x++ {
101 if x == p {
102 prevrev("", x, true)
103 x++
104 }
105
106 if x > p {
107 prevrev(strconv.Itoa(x), x, false)
108 }
109 }
110 }
111
112 // вперёд-назад
113 if c.More {
114 prevrev("| Next =>", p+1, false)
115 }
116
117 return list.String()
118}
119
120func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) string {
51 var list strings.Builder 121 var list strings.Builder
52 list.WriteString(`<div class="content">`) 122 list.WriteString(`<div class="content">`)
53 for _, data := range devs { 123 for _, data := range devs {
@@ -81,6 +151,8 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation) string {
81 list.WriteString("</a></div>") 151 list.WriteString("</a></div>")
82 } 152 }
83 list.WriteString("</div>") 153 list.WriteString("</div>")
154 list.WriteString(s.NavBase(content[0]))
155
84 return list.String() 156 return list.String()
85} 157}
86 158
@@ -102,7 +174,6 @@ func (s skunkyart) Deviation(author, postname string) {
102 // время публикации 174 // время публикации
103 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String() 175 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
104 176
105 println(post.Post.Description)
106 // хештэги 177 // хештэги
107 for _, x := range post.Post.Deviation.Extended.Tags { 178 for _, x := range post.Post.Deviation.Extended.Tags {
108 var tag strings.Builder 179 var tag strings.Builder
@@ -115,34 +186,52 @@ func (s skunkyart) Deviation(author, postname string) {
115 post.Tags += tag.String() 186 post.Tags += tag.String()
116 } 187 }
117 188
189 // FIXME: первый комментарий не отображается.
118 // генерация комментов 190 // генерация комментов
119 var cmmts strings.Builder 191 var cmmts strings.Builder
120 var replied map[int]string 192 replied := make(map[int]string)
121 _ = replied
122 c := devianter.CommentsFunc(id, post.Post.Comments.Cursor, s.Page, 1) 193 c := devianter.CommentsFunc(id, post.Post.Comments.Cursor, s.Page, 1)
123 194
124 cmmts.WriteString("<details><summary>Comments: <b>") 195 cmmts.WriteString("<details><summary>Comments: <b>")
125 cmmts.WriteString(strconv.Itoa(c.Total)) 196 cmmts.WriteString(strconv.Itoa(c.Total))
126 cmmts.WriteString("</b></summary>") 197 cmmts.WriteString("</b></summary>")
127 for _, x := range c.Thread { 198 for _, x := range c.Thread {
128 cmmts.WriteString(`<div class="msg"><p id="`) 199 replied[x.ID] = x.User.Username
200 cmmts.WriteString(`<div class="msg`)
201 if x.Parent > 0 {
202 cmmts.WriteString(` reply`)
203 }
204 cmmts.WriteString(`"><p id="`)
129 cmmts.WriteString(strconv.Itoa(x.ID)) 205 cmmts.WriteString(strconv.Itoa(x.ID))
130 cmmts.WriteString(`"><img src="/media/`) 206 cmmts.WriteString(`"><img src="/media/`)
131 cmmts.WriteString(x.User.Username) 207 cmmts.WriteString(x.User.Username)
132 cmmts.WriteString(`?type=a" width="30px" height="30px"><a href="/user/`) 208 cmmts.WriteString(`?type=a" width="30px" height="30px"><a href="/user/`)
133 cmmts.WriteString(x.User.Username) 209 cmmts.WriteString(x.User.Username)
134 cmmts.WriteString(`"><b`) 210 cmmts.WriteString(`"><b`)
211 cmmts.WriteString(` class="`)
135 if x.User.Banned { 212 if x.User.Banned {
136 cmmts.WriteString(` class="banned"`) 213 cmmts.WriteString(`banned`)
137 } 214 }
138 if x.Author { 215 if x.Author {
139 cmmts.WriteString(` class="author"`) 216 cmmts.WriteString(`author`)
140 } 217 }
141 cmmts.WriteString(">") 218 cmmts.WriteString(`">`)
142 cmmts.WriteString(x.User.Username) 219 cmmts.WriteString(x.User.Username)
143 cmmts.WriteString("</b></a> ") 220 cmmts.WriteString("</b></a> ")
221 if x.Parent > 0 {
222 cmmts.WriteString(` In reply to <a href="#`)
223 cmmts.WriteString(strconv.Itoa(x.Parent))
224 cmmts.WriteString(`">`)
225 if replied[x.Parent] == "" {
226 cmmts.WriteString("???")
227 } else {
228 cmmts.WriteString(replied[x.Parent])
229 }
230 cmmts.WriteString("</a>")
231 }
232 cmmts.WriteString(" [")
144 cmmts.WriteString(x.Posted.UTC().String()) 233 cmmts.WriteString(x.Posted.UTC().String())
145 cmmts.WriteString("<p>") 234 cmmts.WriteString("]<p>")
146 cmmts.WriteString(x.Comment) 235 cmmts.WriteString(x.Comment)
147 cmmts.WriteString("<p>👍: ") 236 cmmts.WriteString("<p>👍: ")
148 cmmts.WriteString(strconv.Itoa(x.Likes)) 237 cmmts.WriteString(strconv.Itoa(x.Likes))
@@ -150,7 +239,12 @@ func (s skunkyart) Deviation(author, postname string) {
150 cmmts.WriteString(strconv.Itoa(x.Replies)) 239 cmmts.WriteString(strconv.Itoa(x.Replies))
151 cmmts.WriteString("</p></div>\n") 240 cmmts.WriteString("</p></div>\n")
152 } 241 }
242 cmmts.WriteString(s.NavBase(dlist{
243 Pages: 0,
244 More: c.HasMore,
245 }))
153 cmmts.WriteString("</details>") 246 cmmts.WriteString("</details>")
247
154 post.Comments = cmmts.String() 248 post.Comments = cmmts.String()
155 249
156 s.exe("html/deviantion.htm", &post) 250 s.exe("html/deviantion.htm", &post)
@@ -159,6 +253,14 @@ func (s skunkyart) Deviation(author, postname string) {
159 } 253 }
160} 254}
161 255
256func (s skunkyart) DD() {
257 dd := devianter.DailyDeviationsFunc(s.Page)
258 s.exe("html/list.htm", s.DeviationList(dd.Deviations, dlist{
259 Pages: 0,
260 More: dd.HasMore,
261 }))
262}
263
162func (s skunkyart) Search() { 264func (s skunkyart) Search() {
163 // тут всё и так понятно 265 // тут всё и так понятно
164 if s.Type == 'a' || s.Type == 't' || s.Type == 'g' { 266 if s.Type == 'a' || s.Type == 't' || s.Type == 'g' {
@@ -170,7 +272,10 @@ func (s skunkyart) Search() {
170 var e error 272 var e error
171 srch.Search, e = devianter.SearchFunc(s.Query, s.Page, s.Type) 273 srch.Search, e = devianter.SearchFunc(s.Query, s.Page, s.Type)
172 err(e) 274 err(e)
173 srch.List = s.DeviationList(srch.Search.Results) 275 srch.List = s.DeviationList(srch.Search.Results, dlist{
276 Pages: srch.Search.Pages,
277 More: srch.Search.HasMore,
278 })
174 279
175 s.exe("html/search.htm", &srch) 280 s.exe("html/search.htm", &srch)
176 } else { 281 } else {
config.example.json added +12
@@ -0,0 +1,12 @@
1{
2 "listen": "0.0.0.0:3003",
3 "base_uri": null,
4 "cache": {
5 "enabled": true,
6 "path": "cache",
7 "lifetime": null,
8 "max_size": 10000
9 },
10 "proxy": false,
11 "nsfw": false
12}\ No newline at end of file
css/skunky.css +5
@@ -47,6 +47,11 @@ form input, button, select {
47.msg:hover { 47.msg:hover {
48 background-color: #134134; 48 background-color: #134134;
49} 49}
50.reply {
51 border-radius: 0px 2px 2px 0px;
52 border-left: #258268 solid;
53 margin-left: 40px;
54}
50.dd { 55.dd {
51 color: rgb(160, 0, 147); 56 color: rgb(160, 0, 147);
52} 57}
html/list.htm added +21
@@ -0,0 +1,21 @@
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>SkunkyArt</title>
5 <link rel="stylesheet" href="gui/css/skunky.css">
6 </head>
7 <main>
8 <header>
9 <h1><a href="/">HOME</a> | <a href="/dd">DD</a></h1>
10 <form method="get" action="/search">
11 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
12 <select name="type">
13 <option value="all">All</option>
14 <option value="tag">Tag</option>
15 </select>
16 <button type="submit">Search!</button>
17 </form>
18 </header>
19 {{.}}
20 </main>
21</html>\ No newline at end of file
html/search.htm −1
@@ -21,7 +21,6 @@
21 <h1>Total resuls: {{.Search.Total}}</h1> 21 <h1>Total resuls: {{.Search.Total}}</h1>
22 {{end}} 22 {{end}}
23 {{.List}} 23 {{.List}}
24 {{.Search.Pages}}
25 {{else}} 24 {{else}}
26 <p>No results :(</p> 25 <p>No results :(</p>
27 {{end}} 26 {{end}}
main.go +7 −1
@@ -1,7 +1,13 @@
1package main 1package main
2 2
3import "skunkyart/app" 3import (
4 "skunkyart/app"
5
6 "git.macaw.me/skunky/devianter"
7)
4 8
5func main() { 9func main() {
10 devianter.UpdateCSRF()
11
6 app.Router() 12 app.Router()
7} 13}