From 49a64e91e76b5970007c0be22b74942fee11df8a Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Mon, 18 Aug 2025 12:32:54 +0700 Subject: feat: add map page --- internal/b2/client.go | 1 + internal/frontmatter/parser.go | 1 - locale/localization.en.yaml | 2 + locale/localization.go | 5 + locale/localization.ru.yaml | 2 + main.go | 67 +++++++++++ static/input.css | 17 +-- views/layouts/general-page.html | 2 +- views/pages/blog-page.html | 10 +- views/pages/global-map.html | 241 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 328 insertions(+), 20 deletions(-) create mode 100644 views/pages/global-map.html diff --git a/internal/b2/client.go b/internal/b2/client.go index 5322b84..7b745a8 100644 --- a/internal/b2/client.go +++ b/internal/b2/client.go @@ -84,6 +84,7 @@ func (c *B2Client) Scan(prefix string) ([]*BlogPage, error) { PublishedTime: publishedTime, Thumbnail: attrs.Info["thumbnail"], Tags: strings.Split(attrs.Info["tags"], ","), + Geolocation: attrs.Info["geolocation"], }, }) } diff --git a/internal/frontmatter/parser.go b/internal/frontmatter/parser.go index 3c00a7f..1a521c0 100644 --- a/internal/frontmatter/parser.go +++ b/internal/frontmatter/parser.go @@ -16,7 +16,6 @@ type Metadata struct { Thumbnail string `yaml:"thumbnail"` Tags []string `yaml:"tags"` Geolocation string `yaml:"geolocation"` - Timezone string `yaml:"timezone"` } func ParseFrontmatter(content []byte) (metadata *Metadata, markdown []byte, err error) { diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml index 879fce8..f5ee6c9 100644 --- a/locale/localization.en.yaml +++ b/locale/localization.en.yaml @@ -7,3 +7,5 @@ BlogSearch: ActionDateOrdered: 'Action Date' PublicationDateOrdered: 'Publication Date' ChooseAllTags: 'Choose All' +GlobalMap: + Header: 'Global Map' diff --git a/locale/localization.go b/locale/localization.go index d5c6b48..56b6462 100644 --- a/locale/localization.go +++ b/locale/localization.go @@ -10,6 +10,7 @@ import ( type LocaleConfig struct { TagsLabel string `yaml:"TagsLabel" json:"TagsLabel"` BlogSearch BlogSearchConfig `yaml:"BlogSearch" json:"BlogSearch"` + GlobalMap GlobalMapConfig `yaml:"GlobalMap" json:"GlobalMap"` } type BlogSearchConfig struct { @@ -22,6 +23,10 @@ type BlogSearchConfig struct { ChooseAllTags string `yaml:"ChooseAllTags" json:"ChooseAllTags"` } +type GlobalMapConfig struct { + Header string `yaml:"Header" json:"Header"` +} + func LoadConfig(path string, config *LocaleConfig) error { fileBytes, err := os.ReadFile(path) if err != nil { diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml index 786665d..c236d1e 100644 --- a/locale/localization.ru.yaml +++ b/locale/localization.ru.yaml @@ -7,3 +7,5 @@ BlogSearch: ActionDateOrdered: 'дате действия' PublicationDateOrdered: 'времени публикации' ChooseAllTags: 'Выбрать все' +GlobalMap: + Header: 'Глобальная карта' diff --git a/main.go b/main.go index 9e3f9a4..e07ca6b 100644 --- a/main.go +++ b/main.go @@ -83,6 +83,7 @@ func main() { {Name: "blog-catalogue", Files: []string{"views/layouts/general-page.html", "views/pages/blog-catalogue.html"}}, {Name: "index", Files: []string{"views/index.html"}}, {Name: "catalogue-blog-cards", Files: []string{"views/partials/catalogue-blog-cards.html", "views/partials/catalogue-blog-card-tags.html"}}, + {Name: "global-map", Files: []string{"views/pages/global-map.html"}}, }) if err != nil { slog.Error("fail to initialize template manager", slog.String("error", err.Error())) @@ -112,6 +113,72 @@ func main() { return c.Type("html").Send(content) }) + app.Get("/:lang/map", func(c *fiber.Ctx) error { + lang := c.Params("lang") + if !slices.Contains(availableLanguages, lang) { + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server does not support '%s' language... yet??", lang)) + } + + pages, err := b2Client.Scan(lang + "/") + status := fiber.StatusOK + if err != nil { + status = fiber.StatusPartialContent + pages = []*b2.BlogPage{} + } + + type MapMarker struct { + Title string `json:"Title"` + PageLink string `json:"PageLink"` + Lat float64 `json:"Lat"` + Long float64 `json:"Long"` + AccuracyMeters int64 `json:"AccuracyMeters"` + Thumbnail string `json:"Thumbnail"` + } + + mapMarkers := make([]*MapMarker, 0, len(pages)) + for _, page := range pages { + geolocationParts := strings.Split(page.Metadata.Geolocation, " ") + if len(geolocationParts) < 2 { + continue + } + + var x, y float64 + var areaError int64 + if len(geolocationParts) >= 2 { + x, _ = strconv.ParseFloat(geolocationParts[0], 64) + y, _ = strconv.ParseFloat(geolocationParts[1], 64) + } + if len(geolocationParts) >= 3 { + areaError, _ = strconv.ParseInt(geolocationParts[2], 10, 64) + } + + mapMarkers = append(mapMarkers, &MapMarker{ + Title: page.Metadata.Title, + PageLink: fmt.Sprintf("/%s/blog/%s", lang, page.FileName), + Lat: x, + Long: y, + AccuracyMeters: areaError, + Thumbnail: page.Metadata.Thumbnail, + }) + } + + content, err := tm.Render("global-map", fiber.Map{ + "Lang": lang, + "L": localization[lang], + "MapLocationLat": 45.4507, + "MapLocationLong": 68.8319, + "MapMarkers": mapMarkers, + }) + if err != nil { + slog.Warn("failed to generate page", slog.String("page", "/"+lang+"/map"), slog.String("error", err.Error())) + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page") + } + + return c.Type("html").Status(status).Send(content) + }) + app.Get("/:lang/blog", func(c *fiber.Ctx) error { lang := c.Params("lang") if !slices.Contains(availableLanguages, lang) { diff --git a/static/input.css b/static/input.css index 00ac12f..0d6e786 100644 --- a/static/input.css +++ b/static/input.css @@ -1,17 +1,6 @@ @import "tailwindcss"; @theme { - --color-olive-dark: #4b513a; - --color-olive-medium: #8a9d8a; - --color-olive-light: #a5b8a5; - --color-cream-dark: #ffffdd; - --color-cream-light: #fffff0; - --color-lettuce-dark: #384a0c; - --color-lettuce-medium: #9bb665; - --color-lettuce-light: #bbd095; - --color-sunny-dark: #f5eee2; - --color-sunny-light: #fff93f; - --font-spectral: Spectral, serif; --font-patua: "Patua One", cursive; } @@ -109,16 +98,16 @@ a:hover { color: var(--main-light-color) !important; } -#location-map { +#map-container { color: var(--leaflet-text-color) !important; } -#location-map a { +#map-container a { color: var(--leaflet-link-color) !important; text-decoration: underline; } -#location-map a:hover { +#map-container a:hover { color: var(--leaflet-link-hover-color) !important; text-decoration: underline; } diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index d2e74fa..5918956 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -44,7 +44,7 @@
- +
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index 330a1ac..755744d 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -19,7 +19,7 @@ {{ .ParsedMarkdown }} {{- if .MapLocationX }}
-
+

{{- end }}
@@ -33,8 +33,10 @@ diff --git a/views/pages/global-map.html b/views/pages/global-map.html new file mode 100644 index 0000000..cbb6c7d --- /dev/null +++ b/views/pages/global-map.html @@ -0,0 +1,241 @@ + + + + + + SAYA TODAY // {{ .L.GlobalMap.Header }} + + + + + {{ block "header" . }}{{ end }} + + + + + + + + + + + + +
+ + + + + + + + + {{ block "footer" . }}{{ end }} + + + -- cgit v1.3.1+13