summaryrefslogtreecommitdiffci
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
-rw-r--r--internal/b2/client.go1
-rw-r--r--internal/frontmatter/parser.go1
-rw-r--r--locale/localization.en.yaml2
-rw-r--r--locale/localization.go5
-rw-r--r--locale/localization.ru.yaml2
-rw-r--r--main.go67
-rw-r--r--static/input.css17
-rw-r--r--views/layouts/general-page.html2
-rw-r--r--views/pages/blog-page.html10
-rw-r--r--views/pages/global-map.html241
10 files changed, 20 insertions, 328 deletions
diff --git a/internal/b2/client.go b/internal/b2/client.go
index 7b745a8..5322b84 100644
--- a/internal/b2/client.go
+++ b/internal/b2/client.go
@@ -84,7 +84,6 @@ 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 1a521c0..3c00a7f 100644
--- a/internal/frontmatter/parser.go
+++ b/internal/frontmatter/parser.go
@@ -16,6 +16,7 @@ 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 f5ee6c9..879fce8 100644
--- a/locale/localization.en.yaml
+++ b/locale/localization.en.yaml
@@ -7,5 +7,3 @@ 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 56b6462..d5c6b48 100644
--- a/locale/localization.go
+++ b/locale/localization.go
@@ -10,7 +10,6 @@ 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 {
@@ -23,10 +22,6 @@ 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 c236d1e..786665d 100644
--- a/locale/localization.ru.yaml
+++ b/locale/localization.ru.yaml
@@ -7,5 +7,3 @@ BlogSearch:
ActionDateOrdered: 'дате действия'
PublicationDateOrdered: 'времени публикации'
ChooseAllTags: 'Выбрать все'
-GlobalMap:
- Header: 'Глобальная карта'
diff --git a/main.go b/main.go
index e07ca6b..9e3f9a4 100644
--- a/main.go
+++ b/main.go
@@ -83,7 +83,6 @@ 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()))
@@ -113,72 +112,6 @@ 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 0d6e786..00ac12f 100644
--- a/static/input.css
+++ b/static/input.css
@@ -1,6 +1,17 @@
@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;
}
@@ -98,16 +109,16 @@ a:hover {
color: var(--main-light-color) !important;
}
-#map-container {
+#location-map {
color: var(--leaflet-text-color) !important;
}
-#map-container a {
+#location-map a {
color: var(--leaflet-link-color) !important;
text-decoration: underline;
}
-#map-container a:hover {
+#location-map 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 5918956..d2e74fa 100644
--- a/views/layouts/general-page.html
+++ b/views/layouts/general-page.html
@@ -44,7 +44,7 @@
<div class="text-[3vw] ar-lt-0.8:text-[5vh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1vw] flex flex-col ar-lt-0.8:flex-row gap-0 relative z-20">
<i onclick="location.href='/';" class="fas fa-home w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
<i onclick="location.href='/{{ .Lang }}/blog';" class="fas fa-search w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
- <i onclick="location.href='/{{ .Lang }}/map';" class="fas fa-map w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
+ <!-- <i class="fas fa-images w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i> -->
<i onclick="toggleThemeWheel();" id="theme-button" class="fas fa-swatchbook w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
</div>
<div class="theme-wheel" id="theme-wheel">
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html
index 755744d..330a1ac 100644
--- a/views/pages/blog-page.html
+++ b/views/pages/blog-page.html
@@ -19,7 +19,7 @@
{{ .ParsedMarkdown }}
{{- if .MapLocationX }}
<hr class="border-t-4 border-dotted border-main-dark mt-[0.8vmin] mb-[0.8vmin] w-[80%] ml-auto mr-auto">
- <div id="map-container" class="relative p-[0.8vmin] flex-none ml-auto mr-auto w-[60%] ar-lt-0.8:w-[80%] h-[40vh] ar-lt-0.8:h-[30vh]"></div>
+ <div id="location-map" class="relative p-[0.8vmin] flex-none ml-auto mr-auto w-[60%] ar-lt-0.8:w-[80%] h-[40vh] ar-lt-0.8:h-[30vh]"></div>
<hr class="border-t-4 border-dotted border-main-dark mt-[0.8vmin] mb-[0.8vmin] w-[80%] ml-auto mr-auto">
{{- end }}
</div>
@@ -33,10 +33,8 @@
<script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.js"></script>
<script>
- var map;
-
function initLocationMap(id, x, y, relativeErrorMeters = 0) {
- map = L.map(id).setView([x, y], 8);
+ var map = L.map(id).setView([x, y], 8);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
@@ -60,7 +58,7 @@
}
document.addEventListener('DOMContentLoaded', () => {initLocationMap(
- 'map-container',
+ 'location-map',
{{ .MapLocationX }},
{{ .MapLocationY }},
{{ .MapLocationAreaMeters }}
@@ -68,7 +66,7 @@
window.addEventListener('resize', function() {
setTimeout(() => {
- map.invalidateSize();
+ if (window.location-map) window.location-map.invalidateSize();
}, 100);
});
</script>
diff --git a/views/pages/global-map.html b/views/pages/global-map.html
deleted file mode 100644
index cbb6c7d..0000000
--- a/views/pages/global-map.html
+++ /dev/null
@@ -1,241 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>SAYA TODAY // {{ .L.GlobalMap.Header }}</title>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&family=Patua+One&display=swap" rel="stylesheet">
- {{ block "header" . }}{{ end }}
- <link href="/output.css" rel="stylesheet">
- <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css">
-</head>
-
-<body data-theme="ram" class="font-spectral text-main-dark overflow-hidden bg-interlocked-hexagons h-screen flex flex-row ar-lt-0.8:flex-col">
-
- <script>
- function switchTheme(theme) {
- if (theme == "" || typeof theme == "undefined") {
- const currentTheme = document.body.getAttribute('data-theme');
- switch (currentTheme) {
- case "olive": theme = "lettuce"; break;
- case "lettuce": theme = "night"; break;
- case "night": theme = "ram"; break;
- case "ram": theme = "olive"; break;
- }
- }
- localStorage.setItem('theme', theme);
- document.body.setAttribute('data-theme', theme);
- }
-
- const savedTheme = localStorage.getItem('theme') || 'ram';
- switchTheme(savedTheme);
- </script>
-
- <script>
- const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- </script>
-
- <div id="sidebar" class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5vw] ar-lt-0.8:w-screen h-screen ar-lt-0.8:h-[8vh]">
- <div class="logo-custom ar-lt-0.8:hidden text-sidebar font-patua font-extrabold text-center relative z-20">
- saya.today
- </div>
- <div class="text-[3vw] ar-lt-0.8:text-[5vh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1vw] flex flex-col ar-lt-0.8:flex-row gap-0 relative z-20">
- <i onclick="location.href='/';" class="fas fa-home w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
- <i onclick="location.href='/{{ .Lang }}/blog';" class="fas fa-search w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
- <i onclick="location.href='/{{ .Lang }}/map';" class="fas fa-map w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
- <i onclick="toggleThemeWheel();" id="theme-button" class="fas fa-swatchbook w-[5vw] ar-lt-0.8:w-[8vh] h-[5vw] ar-lt-0.8:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
- </div>
- <div class="theme-wheel" id="theme-wheel">
- <div class="theme-icon" data-theme="olive"></div>
- <div class="theme-icon" data-theme="lettuce"></div>
- <div class="theme-icon" data-theme="night"></div>
- <div class="theme-icon" data-theme="ram"></div>
- </div>
- </div>
-
- <div id="map-container" class="bg-background-dark flex z-10 w-[95vw] ar-lt-0.8:w-screen h-screen ar-lt-0.8:h-[92vh]"></div>
-
- <script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script>
- <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.js"></script>
-
- <script>
- const themeWheel = document.getElementById('theme-wheel');
- const themeButton = document.getElementById('theme-button');
- const themeIcons = document.querySelectorAll('.theme-icon');
-
- themeIcons.forEach(icon => {
- if (icon.getAttribute('data-theme') == savedTheme) {
- icon.classList.add('active');
- }
- });
-
- function calculateVmax(percent) {
- const viewportWidth = window.innerWidth;
- const viewportHeight = window.innerHeight;
-
- const largerDimension = Math.max(viewportWidth, viewportHeight);
-
- const vmaxValue = (largerDimension / 100) * percent;
-
- return vmaxValue;
- }
-
- function positionThemeIcons() {
- const icons = themeWheel.querySelectorAll('.theme-icon');
- const iconCount = icons.length;
- const vmax = calculateVmax(1);
- const wheelRadius = 7.5*vmax; // Radius of the wheel
- const iconRadius = 4*vmax; // Distance from center to place icons
- const iconSize = 3*vmax; // Half of icon size for centering
-
- icons.forEach((icon, index) => {
- const angle = (index * 360 / iconCount) - 90;
- const angleRad = (angle * Math.PI) / 180;
-
- const x = Math.cos(angleRad) * iconRadius + wheelRadius - iconSize;
- const y = Math.sin(angleRad) * iconRadius + wheelRadius - iconSize;
-
- icon.style.left = x + 'px';
- icon.style.top = y + 'px';
- });
- }
-
- function positionThemeWheel() {
- const buttonRect = themeButton.getBoundingClientRect();
- const buttonCenterX = buttonRect.left + buttonRect.width / 2;
- const buttonCenterY = buttonRect.top + buttonRect.height / 2;
-
- const wheelRadius = calculateVmax(7.5);
- const margin = calculateVmax(1);
-
- let wheelX = buttonCenterX - wheelRadius;
- let wheelY = buttonCenterY - wheelRadius;
-
- const viewportWidth = window.innerWidth;
- const viewportHeight = window.innerHeight;
-
- if (wheelX < margin) {
- wheelX = margin;
- } else if (wheelX + (wheelRadius * 2) > viewportWidth - margin) {
- wheelX = viewportWidth - (wheelRadius * 2) - margin;
- }
-
- if (wheelY < margin) {
- wheelY = margin;
- } else if (wheelY + (wheelRadius * 2) > viewportHeight - margin) {
- wheelY = viewportHeight - (wheelRadius * 2) - margin;
- }
-
- themeWheel.style.left = wheelX + 'px';
- themeWheel.style.top = wheelY + 'px';
- }
-
- function hideThemeWheel() {
- themeWheel.classList.remove('active');
- }
-
- function showThemeWheel() {
- positionThemeWheel();
- positionThemeIcons();
- themeWheel.classList.add('active');
- }
-
- function toggleThemeWheel() {
- if (themeWheel.classList.contains('active')) {
- hideThemeWheel();
- } else {
- showThemeWheel();
- }
- }
-
- themeIcons.forEach(icon => {
- icon.addEventListener('click', (e) => {
- e.stopPropagation();
- const theme = icon.getAttribute('data-theme');
- switchTheme(theme);
- themeIcons.forEach(icon => {
- icon.classList.remove('active');
- })
- icon.classList.add('active');
- });
- });
-
- themeWheel.addEventListener('mouseleave', hideThemeWheel);
-
- document.addEventListener('click', (e) => {
- if (!themeWheel.contains(e.target) && e.target !== themeButton) {
- hideThemeWheel();
- }
- });
- </script>
-
- <script>
- var map;
-
- function initLocationMap() {
- map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4);
-
- L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
- maxZoom: 18,
- attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
- }).addTo(map);
- }
-
- function stringToColor(str) {
- let hash = 0;
- for (let i = 0; i < str.length; i++) {
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
- }
-
- let color = '#';
- for (let i = 0; i < 3; i++) {
- const value = (hash >> (i * 8)) & 0xFF;
- color += ('00' + value.toString(16)).substr(-2);
- }
-
- return color;
- }
-
- function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "") {
- const titleColor = stringToColor(title);
-
- if (relativeErrorMeters != 0) {
- var circle = L.circle([x, y], {
- color: titleColor,
- fillColor: titleColor,
- fillOpacity: 0.15,
- radius: relativeErrorMeters
- }).addTo(map);
- }
-
- var marker = L.marker([x, y], {
- title: title,
- }).addTo(map)
- .bindPopup(`
- <div class="flex flex-row justify-center justify-items-center">
- <img onclick="location.href='${link}';" class="cursor-pointer object-cover rounded-[10%] select-none w-[3vmax] h-[3vmax] mr-[0.5vmax]" src="https://f003.backblazeb2.com/file/sayana-photos/thumbnails/${thumbnail}.webp">
- <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span>
- </div>`);
- }
-
- document.addEventListener('DOMContentLoaded', () => {
- initLocationMap();
- {{- range .MapMarkers }}
- mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }});
- {{- end }}
- });
-
- window.addEventListener('resize', function() {
- setTimeout(() => {
- map.invalidateSize();
- }, 100);
- });
- </script>
-
- {{ block "footer" . }}{{ end }}
-
-</body>
-</html>