From b0ab2ed92870d8c651267db10d68eb695da2ec62 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Tue, 26 Aug 2025 23:41:00 +0700 Subject: feat: like button (only inmem-cache for now) --- views/layouts/general-page.html | 14 ++++++++------ views/pages/blog-catalogue.html | 2 +- views/pages/blog-page.html | 6 +++++- views/pages/global-map.html | 2 -- views/partials/blog-page-like-button.html | 6 ++++++ 5 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 views/partials/blog-page-like-button.html (limited to 'views') diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index cd19bd0..9885d66 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -8,7 +8,7 @@ - {{ block "header" . }}{{ end }} + {{ block "top-embeds" . }}{{ end }} @@ -66,17 +66,19 @@

{{ .Title }}

{{ .PublishedDate }}

+ {{ block "header" . }}{{ end }}
{{ block "body" . }}{{ end }}
-

+ {{ block "footer" . }}{{ end }} +

All the photos on saya.today are licensed under CC BY-SA 4.0 by Saya Andy © {{ .PublishedYear }}

- - - + CC + BY + SA

@@ -209,7 +211,7 @@ }); - {{ block "footer" . }}{{ end }} + {{ block "bottom-embeds" . }}{{ end }} diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 9fa8ea7..25b0685 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -56,7 +56,7 @@ {{ end }} -{{ define "footer" }} +{{ define "bottom-embeds" }} diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 6185076..569166f 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -263,7 +263,5 @@ }); - {{ block "footer" . }}{{ end }} - diff --git a/views/partials/blog-page-like-button.html b/views/partials/blog-page-like-button.html new file mode 100644 index 0000000..50a9d7a --- /dev/null +++ b/views/partials/blog-page-like-button.html @@ -0,0 +1,6 @@ + \ No newline at end of file -- cgit v1.3.1+13 From ea8c6da9b8bafa1d4e5fe5a77fb306006db7d6f6 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Wed, 27 Aug 2025 01:37:04 +0700 Subject: feat: update galleries on resize (with smooth animations this time) --- internal/lightgallery/html_renderer.go | 49 ++++++++++++++++++---------------- views/pages/blog-page.html | 16 +++++++++-- 2 files changed, 40 insertions(+), 25 deletions(-) (limited to 'views') diff --git a/internal/lightgallery/html_renderer.go b/internal/lightgallery/html_renderer.go index e20e9ec..17a6f2b 100644 --- a/internal/lightgallery/html_renderer.go +++ b/internal/lightgallery/html_renderer.go @@ -92,32 +92,35 @@ func (r *LightGalleryHTMLRenderer) renderLightGallery(w util.BufWriter, source [ w.WriteString(fmt.Sprintf(` `, galleryID, galleryID, strings.Join(dynamicElements, ","), galleryID)) +`, galleryID, galleryID, strings.Join(dynamicElements, ","), galleryID, galleryID)) } return ast.WalkContinue, nil diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index 83860d7..d685fda 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -12,6 +12,10 @@ height: 100% !important; } + + {{ end }} {{ define "body" }} @@ -70,10 +74,18 @@ {{ .MapLocationAreaMeters }} )}); + let resizeTimeout; window.addEventListener('resize', function() { - setTimeout(() => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(() => { map.invalidateSize(); - }, 100); + var oldGalleryMap = new Map(galleryMap); + oldGalleryMap.forEach((createFunc, g, map) => { + galleryMap.delete(g); + g.destroy(); + createFunc(); + }); + }, 300); }); {{- end }} -- cgit v1.3.1+13 From 26c77e33356a519b3caaed44fd9d7f8722f41918 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Wed, 27 Aug 2025 19:11:16 +0700 Subject: feat: add and show like count feat: localize like button --- internal/router/api-v1-like.go | 15 ++++++++++----- internal/router/client-cache.go | 7 +++++++ locale/localization.en.yaml | 1 + locale/localization.go | 1 + locale/localization.ru.yaml | 1 + main.go | 4 ++-- views/partials/blog-page-like-button.html | 5 +++-- 7 files changed, 25 insertions(+), 9 deletions(-) (limited to 'views') diff --git a/internal/router/api-v1-like.go b/internal/router/api-v1-like.go index 1732c77..327dafa 100644 --- a/internal/router/api-v1-like.go +++ b/internal/router/api-v1-like.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/SayaAndy/saya-today-web/internal/b2" + "github.com/SayaAndy/saya-today-web/locale" "github.com/gofiber/fiber/v2" ) @@ -15,7 +16,7 @@ func init() { tm.Add("blog-page-like-button", "views/partials/blog-page-like-button.html") } -func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error { +func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) @@ -41,12 +42,12 @@ func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error { return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server did not find '%s' article", pageLink)) } - ip := c.IP() newLikeStatus, err := strconv.ParseBool(c.FormValue("like", "true")) if err != nil { return c.Status(fiber.ErrBadRequest.Code).SendString("invalid 'like' value") } + ip := c.IP() if newLikeStatus { CCache.LikeOn(ip, page) } else { @@ -56,7 +57,9 @@ func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error { slog.Debug("someone pressed the like button!", slog.String("ip", ip), slog.String("page", page), slog.String("new_like_status", fmt.Sprint(newLikeStatus))) if c.Get("HX-Request", "false") == "true" { content, err := tm.Render("blog-page-like-button", fiber.Map{ - "Liked": newLikeStatus, + "L": l[lang], + "Liked": newLikeStatus, + "LikedCount": CCache.GetLikeCount(page), }) if err != nil { slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) @@ -70,7 +73,7 @@ func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error { } } -func Api_V1_Like_Get(b2 *b2.B2Client) func(c *fiber.Ctx) error { +func Api_V1_Like_Get(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) @@ -102,7 +105,9 @@ func Api_V1_Like_Get(b2 *b2.B2Client) func(c *fiber.Ctx) error { slog.Debug("someone requested the like status!", slog.String("ip", ip), slog.String("page", page), slog.Bool("like_status", likeStatus)) if c.Get("HX-Request", "false") == "true" { content, err := tm.Render("blog-page-like-button", fiber.Map{ - "Liked": likeStatus, + "L": l[lang], + "Liked": likeStatus, + "LikedCount": CCache.GetLikeCount(page), }) if err != nil { slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go index 3104a66..7d276b1 100644 --- a/internal/router/client-cache.go +++ b/internal/router/client-cache.go @@ -58,6 +58,13 @@ func (c *ClientCache) GetLikeStatus(id string, page string) bool { return ok } +func (c *ClientCache) GetLikeCount(page string) int { + if userSet, ok := c.likePageMap[page]; ok { + return len(userSet) + } + return 0 +} + func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) { if _, ok := c.mutexLikeMap[id]; !ok { c.mutexLikeMap[id] = &sync.Mutex{} diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml index f5ee6c9..48ef9da 100644 --- a/locale/localization.en.yaml +++ b/locale/localization.en.yaml @@ -9,3 +9,4 @@ BlogSearch: ChooseAllTags: 'Choose All' GlobalMap: Header: 'Global Map' +LikeButton: 'Like!' diff --git a/locale/localization.go b/locale/localization.go index 56b6462..b557e08 100644 --- a/locale/localization.go +++ b/locale/localization.go @@ -11,6 +11,7 @@ type LocaleConfig struct { TagsLabel string `yaml:"TagsLabel" json:"TagsLabel"` BlogSearch BlogSearchConfig `yaml:"BlogSearch" json:"BlogSearch"` GlobalMap GlobalMapConfig `yaml:"GlobalMap" json:"GlobalMap"` + LikeButton string `yaml:"LikeButton" json:"LikeButton"` } type BlogSearchConfig struct { diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml index c236d1e..d6e9fe4 100644 --- a/locale/localization.ru.yaml +++ b/locale/localization.ru.yaml @@ -9,3 +9,4 @@ BlogSearch: ChooseAllTags: 'Выбрать все' GlobalMap: Header: 'Глобальная карта' +LikeButton: 'Нраица!' diff --git a/main.go b/main.go index 1853721..36463ff 100644 --- a/main.go +++ b/main.go @@ -88,8 +88,8 @@ func main() { app.Get("/:lang/blog/:title", router.Lang_Blog_Title(localization, availableLanguages, b2Client, md)) app.Get("/api/v1/tz", router.Api_V1_TZ()) app.Get("/api/v1/blog-search", router.Api_V1_BlogSearch(localization, availableLanguages, b2Client)) - app.Get("/api/v1/like", router.Api_V1_Like_Get(b2Client)) - app.Put("/api/v1/like", router.Api_V1_Like_Put(b2Client)) + app.Get("/api/v1/like", router.Api_V1_Like_Get(localization, b2Client)) + app.Put("/api/v1/like", router.Api_V1_Like_Put(localization, b2Client)) app.Static("/", "./static") diff --git a/views/partials/blog-page-like-button.html b/views/partials/blog-page-like-button.html index 50a9d7a..afc3027 100644 --- a/views/partials/blog-page-like-button.html +++ b/views/partials/blog-page-like-button.html @@ -1,6 +1,7 @@ \ No newline at end of file -- cgit v1.3.1+13 From 5e40ca4d314a9dc6cb7cb2c69081876f5aa446f3 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Fri, 29 Aug 2025 02:02:52 +0700 Subject: feat: implement decor of night/ram theme on general pages --- static/input.css | 14 ++++- views/layouts/general-page.html | 126 ++++++++++++++++++++++++++++++++++++---- views/pages/blog-page.html | 6 +- 3 files changed, 131 insertions(+), 15 deletions(-) (limited to 'views') diff --git a/static/input.css b/static/input.css index 18910eb..7b7da23 100644 --- a/static/input.css +++ b/static/input.css @@ -612,7 +612,7 @@ body[data-theme~="ram"] .leaflet-tile { } } -.star { +.night-star { color: var(--color-yellow-100); animation-name: blink; animation-duration: 5s; @@ -621,6 +621,18 @@ body[data-theme~="ram"] .leaflet-tile { -webkit-user-select: none; } +.ram-frame { + width: 20vmin; + height: 20vmin; + user-select: none; + -webkit-user-select: none; + box-shadow: + inset 1vmin 1vmin var(--background-dark-color), + inset -1vmin -1vmin var(--main-light-color), + 1vmin 1vmin var(--background-dark-color), + -1vmin -1vmin var(--main-dark-color); +} + @keyframes blink { 0% { opacity: 1; diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index 9885d66..a7cf66e 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -15,6 +15,112 @@ + + {{ block "bottom-embeds" . }}{{ end }} diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index d685fda..65ee31d 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -74,10 +74,10 @@ {{ .MapLocationAreaMeters }} )}); - let resizeTimeout; + let galleryResizeTimeout; window.addEventListener('resize', function() { - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(() => { + clearTimeout(galleryResizeTimeout); + galleryResizeTimeout = setTimeout(() => { map.invalidateSize(); var oldGalleryMap = new Map(galleryMap); oldGalleryMap.forEach((createFunc, g, map) => { -- cgit v1.3.1+13 From 218fc8b0d470a3df8a412f7ab32925e74ca4b315 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Sat, 30 Aug 2025 15:13:27 +0700 Subject: feat: like count shown on blog search fix: corrupted page links in cache --- internal/router/api-v1-blog-search.go | 1 + internal/router/client-cache.go | 94 ++++++++++++++++++++------------ internal/router/lang-blog.go | 2 +- views/partials/catalogue-blog-cards.html | 3 + 4 files changed, 64 insertions(+), 36 deletions(-) (limited to 'views') diff --git a/internal/router/api-v1-blog-search.go b/internal/router/api-v1-blog-search.go index a809783..e22fabe 100644 --- a/internal/router/api-v1-blog-search.go +++ b/internal/router/api-v1-blog-search.go @@ -69,6 +69,7 @@ func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []string, b2Clie "ShortDescription": page.Metadata.ShortDescription, "Thumbnail": page.Metadata.Thumbnail, "Tags": page.Metadata.Tags, + "LikeCount": CCache.GetLikeCount(page.FileName), }) break } diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go index b930d5a..81d7641 100644 --- a/internal/router/client-cache.go +++ b/internal/router/client-cache.go @@ -18,11 +18,14 @@ type PageLike struct { type ClientCache struct { hashMap map[string]string - mutexLikeMap map[string]*sync.Mutex - mutexHashMap map[string]*sync.Mutex - likePageMap map[string]map[string]struct{} - salt []byte - db *sql.DB + hashMapMutex sync.RWMutex + + likePageMap map[string]map[string]struct{} + pageMutexMap map[string]*sync.RWMutex + pageMutexMapMutex sync.Mutex + + salt []byte + db *sql.DB } var CCache *ClientCache @@ -40,20 +43,21 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { } likePageMap := make(map[string]map[string]struct{}) + pageMutexMap := make(map[string]*sync.RWMutex) for rows.Next() { - pageRef := make([]byte, 32) - userId := make([]byte, 32) + var pageRef string + var userId []byte if err = rows.Scan(&pageRef, &userId); err != nil { tx.Rollback() return nil, fmt.Errorf("fail scanning blog_likes to fill cache: %w", err) } - pageRefString := string(pageRef) userIdString := base64.RawStdEncoding.EncodeToString(userId) - if _, ok := likePageMap[pageRefString]; !ok { - likePageMap[pageRefString] = make(map[string]struct{}) + if _, ok := likePageMap[pageRef]; !ok { + likePageMap[pageRef] = make(map[string]struct{}) + pageMutexMap[pageRef] = &sync.RWMutex{} } - likePageMap[pageRefString][userIdString] = struct{}{} + likePageMap[pageRef][userIdString] = struct{}{} } if err = tx.Commit(); err != nil { @@ -62,9 +66,8 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { return &ClientCache{ hashMap: make(map[string]string), - mutexLikeMap: make(map[string]*sync.Mutex), - mutexHashMap: make(map[string]*sync.Mutex), likePageMap: likePageMap, + pageMutexMap: pageMutexMap, salt: salt, db: db, }, nil @@ -87,11 +90,9 @@ func (c *ClientCache) Close() error { INSERT OR IGNORE INTO blog_likes (page_ref, user_id) VALUES %s(?, ?); `, strings.Repeat("(?, ?), ", 99)) - sqlStatementVars := make([]interface{}, 0, 200) + sqlStatementVars := make([]any, 0, 200) for pageRef, userSet := range c.likePageMap { - pageRefBytes := []byte(pageRef) - for userId := range userSet { if _, ok := userIdBytes[userId]; !ok { userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId) @@ -101,7 +102,7 @@ func (c *ClientCache) Close() error { } } - sqlStatementVars = append(sqlStatementVars, interface{}(pageRefBytes), interface{}(userIdBytes[userId])) + sqlStatementVars = append(sqlStatementVars, any(pageRef), any(userIdBytes[userId])) if len(sqlStatementVars) < 200 { continue } @@ -110,7 +111,7 @@ func (c *ClientCache) Close() error { slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error())) } - sqlStatementVars = make([]interface{}, 0, 200) + sqlStatementVars = make([]any, 0, 200) } } @@ -129,16 +130,16 @@ func (c *ClientCache) Close() error { } func (c *ClientCache) GetHash(id string) string { + c.hashMapMutex.RLock() if val, ok := c.hashMap[id]; ok { + c.hashMapMutex.RUnlock() slog.Debug("gave an old hash", slog.String("hash", val)) return val } + c.hashMapMutex.RUnlock() - if _, ok := c.mutexHashMap[id]; !ok { - c.mutexHashMap[id] = &sync.Mutex{} - } - c.mutexHashMap[id].Lock() - defer c.mutexHashMap[id].Unlock() + c.hashMapMutex.Lock() + defer c.hashMapMutex.Unlock() if val, ok := c.hashMap[id]; ok { slog.Debug("gave a newly generated hash", slog.String("hash", val)) @@ -150,7 +151,25 @@ func (c *ClientCache) GetHash(id string) string { return c.hashMap[id] } +func (c *ClientCache) getPageMutex(page string) *sync.RWMutex { + c.pageMutexMapMutex.Lock() + defer c.pageMutexMapMutex.Unlock() + + if mutex, ok := c.pageMutexMap[page]; ok { + return mutex + } + + c.pageMutexMap[page] = &sync.RWMutex{} + return c.pageMutexMap[page] +} + func (c *ClientCache) GetLikeStatus(id string, page string) bool { + page = strings.Clone(page) + + mutex := c.getPageMutex(page) + mutex.RLock() + defer mutex.RUnlock() + if _, ok := c.likePageMap[page]; !ok { return false } @@ -159,6 +178,12 @@ func (c *ClientCache) GetLikeStatus(id string, page string) bool { } func (c *ClientCache) GetLikeCount(page string) int { + page = strings.Clone(page) + + mutex := c.getPageMutex(page) + mutex.RLock() + defer mutex.RUnlock() + if userSet, ok := c.likePageMap[page]; ok { return len(userSet) } @@ -166,14 +191,13 @@ func (c *ClientCache) GetLikeCount(page string) int { } func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) { - if _, ok := c.mutexLikeMap[id]; !ok { - c.mutexLikeMap[id] = &sync.Mutex{} - } - c.mutexLikeMap[id].Lock() - defer c.mutexLikeMap[id].Unlock() - + page = strings.Clone(page) hash := c.GetHash(id) + mutex := c.getPageMutex(page) + mutex.Lock() + defer mutex.Unlock() + if userSet, ok := c.likePageMap[page]; ok { _, alreadyLiked = userSet[hash] c.likePageMap[page][hash] = struct{}{} @@ -186,16 +210,16 @@ func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) { } func (c *ClientCache) LikeOff(id string, page string) (alreadyUnliked bool) { - if _, ok := c.mutexLikeMap[id]; !ok { - c.mutexLikeMap[id] = &sync.Mutex{} - } - c.mutexLikeMap[id].Lock() - defer c.mutexLikeMap[id].Unlock() + page = strings.Clone(page) + hash := c.GetHash(id) + + mutex := c.getPageMutex(page) + mutex.Lock() + defer mutex.Unlock() if _, ok := c.likePageMap[page]; !ok { return true } - hash := c.GetHash(id) if _, ok := c.likePageMap[page][hash]; !ok { return true } diff --git a/internal/router/lang-blog.go b/internal/router/lang-blog.go index e5220c6..dd3f079 100644 --- a/internal/router/lang-blog.go +++ b/internal/router/lang-blog.go @@ -83,7 +83,7 @@ func Lang_Blog(l map[string]*locale.LocaleConfig, langs []string, b2Client *b2.B "Title": l[lang].BlogSearch.Header, }) if err != nil { - slog.Warn("failed to generate page", slog.String("page", "/"+lang+"/blog"), slog.String("error", err.Error())) + slog.Warn("failed to generate page", slog.String("path", c.Path()), slog.String("error", err.Error())) c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page") } diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html index 62e931a..35c7ad7 100644 --- a/views/partials/catalogue-blog-cards.html +++ b/views/partials/catalogue-blog-cards.html @@ -8,6 +8,9 @@ {{ .Title }} // {{ .ActionDate }} + // + + {{ .LikeCount }}

{{ .PublishedTime }}

-- cgit v1.3.1+13 From 1c14a4088b068fefe425cad1acdde2d603bd27a9 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Sat, 30 Aug 2025 16:00:28 +0700 Subject: feat: always show search buttons out of overflow feat: add favicon --- static/favicon.ico | Bin 0 -> 5694 bytes static/input.css | 4 +++ views/pages/blog-catalogue.html | 75 +++++++++++++++++++++------------------- 3 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 static/favicon.ico (limited to 'views') diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..33eb226 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/input.css b/static/input.css index f254970..47e7d16 100644 --- a/static/input.css +++ b/static/input.css @@ -581,6 +581,10 @@ body[data-theme~="ram"] .leaflet-tile { width: 100vw; } + .ar-lt-0\.8\:max-h-\[25\%\] { + max-height: 25%; + } + .ar-lt-0\.8\:max-h-\[80\%\] { max-height: 80%; } diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 25b0685..1d0a2de 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -1,44 +1,47 @@ {{ define "body" }}
-
- {{ .L.BlogSearch.TagsHeader }} -
-
- + class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] ar-lt-0.8:max-h-[25%] flex shrink-0 flex-col ar-gt-0.8:mr-[1.2vmin]"> +
+
+ {{ .L.BlogSearch.TagsHeader }} +
+
+ +
+ {{- range .Tags }} +
+ +
+ {{- end }}
- {{- range .Tags }} -
- +
+
+ {{ .L.BlogSearch.OrderByHeader }} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
- {{- end }} -
-
-
- {{ .L.BlogSearch.OrderByHeader }} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
+ +
+
+