summaryrefslogtreecommitdiffci
diff options
from:
to:
context:
space:
mode:
authorGravatar SayaAndy <saya.andy@posteo.com> 2025-09-06 19:18:00 +0700
committerGravatar SayaAndy <saya.andy@posteo.com> 2025-09-06 19:18:00 +0700
commit13a66428533c5f870901343dd505bcb835015430 (patch)
tree5c63fabb02fa9c55c9e7cf2c26c2a35a90806f35
parent0802dc3a8835bc19cf994f9eb2b462df604d4262 (diff)
downloadweb-13a66428533c5f870901343dd505bcb835015430.tar.gz
web-13a66428533c5f870901343dd505bcb835015430.zip
feat: home page
feat: my random fact giver for a home page feat: stricter cors policy refactor: move lightlibrary css onto my bucket
-rw-r--r--config/config.go6
-rw-r--r--config/config.local.yaml10
-rw-r--r--config/config.prod.yaml10
-rw-r--r--config/config.stage.yaml10
-rw-r--r--internal/factgiver/factgiver.go67
-rw-r--r--internal/frontmatter/parser.go2
-rw-r--r--internal/router/api-v1-general-page-body.go13
-rw-r--r--internal/router/api-v1-general-page-bottom-embeds.go4
-rw-r--r--internal/router/api-v1-general-page-footer.go4
-rw-r--r--internal/router/api-v1-general-page-header.go5
-rw-r--r--internal/router/api-v1-general-page-top-embeds.go4
-rw-r--r--internal/router/api-v1-general-page.go2
-rw-r--r--internal/templatemanager/templatemanager.go19
-rw-r--r--locale/localization.en.yaml12
-rw-r--r--locale/localization.go15
-rw-r--r--locale/localization.ru.yaml12
-rw-r--r--main.go20
-rw-r--r--static/input.css28
-rw-r--r--views/index.html2
-rw-r--r--views/layouts/general-page.html8
-rw-r--r--views/pages/blog-page.html9
-rw-r--r--views/pages/global-map.html2
-rw-r--r--views/pages/home-page.html99
23 files changed, 335 insertions, 28 deletions
diff --git a/config/config.go b/config/config.go
index 7438fe1..f57b5cd 100644
--- a/config/config.go
+++ b/config/config.go
@@ -11,6 +11,7 @@ import (
type Config struct {
LogLevel slog.Level `json:"LogLevel" yaml:"logLevel" validate:"required"`
BlogPages BlogPagesConfig `json:"BlogPages" yaml:"blogPages" validate:"required"`
+ FactGiver FactGiverConfig `json:"FactGiver" yaml:"factGiver" validate:"required"`
LocalePath string `json:"LocalePath" yaml:"localePath" validate:"required,filepath"`
AvailableLanguages []AvailableLanguageConfig `json:"AvailableLanguages" yaml:"availableLanguages" validate:"required"`
Auth AuthConfig `json:"Auth" yaml:"auth" validate:"required"`
@@ -33,6 +34,11 @@ type B2Config struct {
ApplicationKey string `json:"ApplicationKey" yaml:"applicationKey"`
}
+type FactGiverConfig struct {
+ Storage StorageConfig `json:"Storage" yaml:"storage" validate:"required"`
+ FactsFileName string `json:"FactsFileName" yaml:"factsFileName" validate:"required"`
+}
+
type AvailableLanguageConfig struct {
Name string `json:"Name" yaml:"name" validate:"required"`
Alt string `json:"Alt" yaml:"alt"`
diff --git a/config/config.local.yaml b/config/config.local.yaml
index 0b48e47..6d0abf3 100644
--- a/config/config.local.yaml
+++ b/config/config.local.yaml
@@ -8,6 +8,16 @@ blogPages:
prefix: 'stage-'
keyID: '${B2_KEY_ID}'
applicationKey: '${B2_APPLICATION_KEY}'
+factGiver:
+ storage:
+ type: b2
+ config:
+ bucketName: sayana-pages
+ region: eu-central-003
+ prefix: ''
+ keyID: '${B2_KEY_ID}'
+ applicationKey: '${B2_APPLICATION_KEY}'
+ factsFileName: 'facts-*.txt'
localePath: ./locale/
availableLanguages:
- name: ru
diff --git a/config/config.prod.yaml b/config/config.prod.yaml
index e1294c0..38981a8 100644
--- a/config/config.prod.yaml
+++ b/config/config.prod.yaml
@@ -8,6 +8,16 @@ blogPages:
prefix: 'prod-'
keyID: '${B2_KEY_ID}'
applicationKey: '${B2_APPLICATION_KEY}'
+factGiver:
+ storage:
+ type: b2
+ config:
+ bucketName: sayana-pages
+ region: eu-central-003
+ prefix: ''
+ keyID: '${B2_KEY_ID}'
+ applicationKey: '${B2_APPLICATION_KEY}'
+ factsFileName: 'facts-*.txt'
localePath: ./locale/
availableLanguages:
- name: ru
diff --git a/config/config.stage.yaml b/config/config.stage.yaml
index 30233c1..b05edaf 100644
--- a/config/config.stage.yaml
+++ b/config/config.stage.yaml
@@ -8,6 +8,16 @@ blogPages:
prefix: 'stage-'
keyID: '${B2_KEY_ID}'
applicationKey: '${B2_APPLICATION_KEY}'
+factGiver:
+ storage:
+ type: b2
+ config:
+ bucketName: sayana-pages
+ region: eu-central-003
+ prefix: ''
+ keyID: '${B2_KEY_ID}'
+ applicationKey: '${B2_APPLICATION_KEY}'
+ factsFileName: 'facts-*.txt'
localePath: ./locale/
availableLanguages:
- name: ru
diff --git a/internal/factgiver/factgiver.go b/internal/factgiver/factgiver.go
new file mode 100644
index 0000000..6f77403
--- /dev/null
+++ b/internal/factgiver/factgiver.go
@@ -0,0 +1,67 @@
+package factgiver
+
+import (
+ "fmt"
+ "math/rand"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/SayaAndy/saya-today-web/config"
+ "github.com/SayaAndy/saya-today-web/internal/b2"
+)
+
+type FactGiver struct {
+ b2Client *b2.B2Client
+ cache map[string][]string
+ langs []string
+ factsFileName string
+ nlRe *regexp.Regexp
+ randGen *rand.Rand
+}
+
+func NewFactGiver(cfg *config.FactGiverConfig, langs []string) (*FactGiver, error) {
+ b2Client, err := b2.NewB2Client(&cfg.Storage.Config)
+ if err != nil {
+ return nil, fmt.Errorf("fail to init b2 client for a new fact giver: %s", err.Error())
+ }
+
+ factGiver := &FactGiver{
+ b2Client: b2Client,
+ cache: make(map[string][]string, len(langs)),
+ langs: langs,
+ factsFileName: cfg.FactsFileName,
+ nlRe: regexp.MustCompile(`\r?\n`),
+ randGen: rand.New(rand.NewSource(time.Now().UnixNano())),
+ }
+ if err = factGiver.initCache(); err != nil {
+ return nil, fmt.Errorf("fail to init cache for a new fact giver: %s", err.Error())
+ }
+
+ return factGiver, nil
+}
+
+func (g *FactGiver) Give(lang string) [3]string {
+ factSlice := make([]string, len(g.cache[lang]))
+ copy(factSlice, g.cache[lang])
+ g.randGen.Shuffle(len(factSlice), func(i, j int) {
+ factSlice[i], factSlice[j] = factSlice[j], factSlice[i]
+ })
+ return [3]string{factSlice[0], factSlice[1], factSlice[2]}
+}
+
+func (g *FactGiver) initCache() error {
+ for _, lang := range g.langs {
+ localFacts := strings.Replace(g.factsFileName, "*", lang, 1)
+ factsContentBytes, err := g.b2Client.ReadAll(localFacts)
+ if err != nil {
+ return fmt.Errorf("fail to read '%s' facts file: %s", lang, err.Error())
+ }
+ factsContent := string(factsContentBytes)
+ g.cache[lang] = g.nlRe.Split(factsContent, -1)
+ if g.cache[lang][len(g.cache[lang])-1] == "" {
+ g.cache[lang] = g.cache[lang][:len(g.cache[lang])-1]
+ }
+ }
+ return nil
+}
diff --git a/internal/frontmatter/parser.go b/internal/frontmatter/parser.go
index 1a521c0..83312dc 100644
--- a/internal/frontmatter/parser.go
+++ b/internal/frontmatter/parser.go
@@ -16,6 +16,8 @@ type Metadata struct {
Thumbnail string `yaml:"thumbnail"`
Tags []string `yaml:"tags"`
Geolocation string `yaml:"geolocation"`
+ Medley string `yaml:"medley"`
+ MedleyPart int `yaml:"medleyPart"`
}
func ParseFrontmatter(content []byte) (metadata *Metadata, markdown []byte, err error) {
diff --git a/internal/router/api-v1-general-page-body.go b/internal/router/api-v1-general-page-body.go
index cdb558a..f27e0c0 100644
--- a/internal/router/api-v1-general-page-body.go
+++ b/internal/router/api-v1-general-page-body.go
@@ -4,6 +4,7 @@ import (
"fmt"
"html/template"
"log/slog"
+ "math/rand"
"net/url"
"regexp"
"slices"
@@ -11,11 +12,14 @@ import (
"time"
"github.com/SayaAndy/saya-today-web/internal/b2"
+ "github.com/SayaAndy/saya-today-web/internal/factgiver"
"github.com/SayaAndy/saya-today-web/locale"
"github.com/gofiber/fiber/v2"
"github.com/yuin/goldmark"
)
+var FactGiver *factgiver.FactGiver
+
func init() {
tm.Add("general-page-body", "views/partials/general-page-body.html")
}
@@ -42,7 +46,7 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []string,
}
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/...'")
}
@@ -134,6 +138,13 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []string,
values["ParsedMarkdown"] = template.HTML(parsedMarkdown)
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
+ } else if len(pathParts) == 1 {
+ values["Title"] = l[lang].HomePage.Header
+ values["FilledHeartCount"] = uint(40)
+ values["OutlineHeartCount"] = uint(40)
+ values["GifName"] = fmt.Sprintf("otter-%d.gif", rand.Int()%3+1)
+ values["FunFacts"] = FactGiver.Give(lang)
+ additionalTemplates = append(additionalTemplates, "views/pages/home-page.html")
}
content, err := tm.Render("general-page-body", values, additionalTemplates...)
diff --git a/internal/router/api-v1-general-page-bottom-embeds.go b/internal/router/api-v1-general-page-bottom-embeds.go
index df766a7..c4d9543 100644
--- a/internal/router/api-v1-general-page-bottom-embeds.go
+++ b/internal/router/api-v1-general-page-bottom-embeds.go
@@ -38,7 +38,7 @@ func Api_V1_GeneralPage_BottomEmbeds(l map[string]*locale.LocaleConfig, langs []
}
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/...'")
}
@@ -58,6 +58,8 @@ func Api_V1_GeneralPage_BottomEmbeds(l map[string]*locale.LocaleConfig, langs []
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
+ } else if len(pathParts) == 1 {
+ additionalTemplates = append(additionalTemplates, "views/pages/home-page.html")
}
content, err := tm.Render("general-page-bottom-embeds", values, additionalTemplates...)
diff --git a/internal/router/api-v1-general-page-footer.go b/internal/router/api-v1-general-page-footer.go
index 98da6bb..003f54c 100644
--- a/internal/router/api-v1-general-page-footer.go
+++ b/internal/router/api-v1-general-page-footer.go
@@ -38,7 +38,7 @@ func Api_V1_GeneralPage_Footer(l map[string]*locale.LocaleConfig, langs []string
}
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/...'")
}
@@ -58,6 +58,8 @@ func Api_V1_GeneralPage_Footer(l map[string]*locale.LocaleConfig, langs []string
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
+ } else if len(pathParts) == 1 {
+ additionalTemplates = append(additionalTemplates, "views/pages/home-page.html")
}
content, err := tm.Render("general-page-footer", values, additionalTemplates...)
diff --git a/internal/router/api-v1-general-page-header.go b/internal/router/api-v1-general-page-header.go
index 1d65966..80228f7 100644
--- a/internal/router/api-v1-general-page-header.go
+++ b/internal/router/api-v1-general-page-header.go
@@ -39,7 +39,7 @@ func Api_V1_GeneralPage_Header(l map[string]*locale.LocaleConfig, langs []string
}
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/...'")
}
@@ -67,6 +67,9 @@ func Api_V1_GeneralPage_Header(l map[string]*locale.LocaleConfig, langs []string
values["PublishedDate"] = metadata.PublishedTime.Format("2006-01-02 15:04:05 -07:00")
values["ActionDate"] = metadata.ActionDate
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
+ } else if len(pathParts) == 1 {
+ values["Title"] = l[lang].HomePage.Header
+ additionalTemplates = append(additionalTemplates, "views/pages/home-page.html")
}
content, err := tm.Render("general-page-header", values, additionalTemplates...)
diff --git a/internal/router/api-v1-general-page-top-embeds.go b/internal/router/api-v1-general-page-top-embeds.go
index b26675f..78021b2 100644
--- a/internal/router/api-v1-general-page-top-embeds.go
+++ b/internal/router/api-v1-general-page-top-embeds.go
@@ -38,7 +38,7 @@ func Api_V1_GeneralPage_TopEmbeds(l map[string]*locale.LocaleConfig, langs []str
}
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/...'")
}
@@ -58,6 +58,8 @@ func Api_V1_GeneralPage_TopEmbeds(l map[string]*locale.LocaleConfig, langs []str
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
+ } else if len(pathParts) == 1 {
+ additionalTemplates = append(additionalTemplates, "views/pages/home-page.html")
}
content, err := tm.Render("general-page-top-embeds", values, additionalTemplates...)
diff --git a/internal/router/api-v1-general-page.go b/internal/router/api-v1-general-page.go
index 8e1387b..d0ea559 100644
--- a/internal/router/api-v1-general-page.go
+++ b/internal/router/api-v1-general-page.go
@@ -20,7 +20,7 @@ func Api_V1_GeneralPage(l map[string]*locale.LocaleConfig, langs []string) func(
path := c.Path()
pathParts := strings.Split(strings.Trim(path, "/"), "/")
- if len(pathParts) < 2 {
+ if len(pathParts) == 0 {
return c.Status(fiber.ErrBadRequest.Code).SendString("url path is invalid: expect format '/{lang}/...'")
}
diff --git a/internal/templatemanager/templatemanager.go b/internal/templatemanager/templatemanager.go
index d8adab6..4bf9a49 100644
--- a/internal/templatemanager/templatemanager.go
+++ b/internal/templatemanager/templatemanager.go
@@ -22,13 +22,22 @@ type TemplateManagerTemplates struct {
Files []string
}
+var templateFuncMap = template.FuncMap{
+ "contains": strings.Contains,
+ "iterate": func(count uint) []uint {
+ items := make([]uint, count)
+ for i := range count {
+ items[i] = i
+ }
+ return items
+ },
+}
+
func NewTemplateManager(templates ...TemplateManagerTemplates) (*TemplateManager, error) {
templateMap := make(map[string]templateManagerRender)
for _, tmplStruct := range templates {
- tmpl := template.New("").Funcs(template.FuncMap{
- "contains": strings.Contains,
- })
+ tmpl := template.New("").Funcs(templateFuncMap)
tmpl, err := tmpl.ParseFiles(tmplStruct.Files...)
if err != nil {
return nil, err
@@ -75,9 +84,7 @@ func (tm *TemplateManager) Add(name string, files ...string) error {
return fmt.Errorf("you can't add template without any files")
}
- tmpl := template.New("").Funcs(template.FuncMap{
- "contains": strings.Contains,
- })
+ tmpl := template.New("").Funcs(templateFuncMap)
tmpl, err := tmpl.ParseFiles(files...)
if err != nil {
diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml
index f5ee6c9..b3fca58 100644
--- a/locale/localization.en.yaml
+++ b/locale/localization.en.yaml
@@ -9,3 +9,15 @@ BlogSearch:
ChooseAllTags: 'Choose All'
GlobalMap:
Header: 'Global Map'
+HomePage:
+ Header: 'Home Page'
+ DidYouKnowThat: 'Did you know, that...'
+ SidebarDescription: 'Describing the Sidebar'
+ HomePageDescription: 'Home Page -- where we are right now!'
+ BlogSearchDescription: 'Blog Search. You can find my travel notes there and, maybe, something else!'
+ MarkerMapDescription: 'Map with markers of my travel notes -- a convinient way to find an interesting region and find related posts!'
+ ThemeSwitchDescription: 'Theme Palette lets you change the theme of the site -- there are 2 dark and 2 bright themes currently!'
+ Hymn1: 'We welcome you to Sayasite!'
+ Hymn2: "We've been expecting you!"
+ Hymn3: 'You bring such joy in Sayasite!'
+ Hymn4: 'No matter where you roam, know our love is true!~'
diff --git a/locale/localization.go b/locale/localization.go
index 56b6462..5917932 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"`
+ HomePage HomePageConfig `yaml:"HomePage" json:"HomePage"`
}
type BlogSearchConfig struct {
@@ -27,6 +28,20 @@ type GlobalMapConfig struct {
Header string `yaml:"Header" json:"Header"`
}
+type HomePageConfig struct {
+ Header string `yaml:"Header" json:"Header"`
+ DidYouKnowThat string `yaml:"DidYouKnowThat" json:"DidYouKnowThat"`
+ SidebarDescription string `yaml:"SidebarDescription" json:"SidebarDescription"`
+ HomePageDescription string `yaml:"HomePageDescription" json:"HomePageDescription"`
+ BlogSearchDescription string `yaml:"BlogSearchDescription" json:"BlogSearchDescription"`
+ MarkerMapDescription string `yaml:"MarkerMapDescription" json:"MarkerMapDescription"`
+ ThemeSwitchDescription string `yaml:"ThemeSwitchDescription" json:"ThemeSwitchDescription"`
+ Hymn1 string `yaml:"Hymn1" json:"Hymn1"`
+ Hymn2 string `yaml:"Hymn2" json:"Hymn2"`
+ Hymn3 string `yaml:"Hymn3" json:"Hymn3"`
+ Hymn4 string `yaml:"Hymn4" json:"Hymn4"`
+}
+
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..5e8d39f 100644
--- a/locale/localization.ru.yaml
+++ b/locale/localization.ru.yaml
@@ -9,3 +9,15 @@ BlogSearch:
ChooseAllTags: 'Выбрать все'
GlobalMap:
Header: 'Глобальная карта'
+HomePage:
+ Header: 'Домашняя страница'
+ DidYouKnowThat: 'А вы знали, что...'
+ SidebarDescription: 'Поясняем за сайдбар'
+ HomePageDescription: 'Домашняя страница -- где мы сейчас и находимся!'
+ BlogSearchDescription: 'Поиск по блогу. Там размещены мои путевые заметки и, может, что-то ещё!'
+ MarkerMapDescription: 'Карта с маркерами моих путевых заметок, -- удобный способ найти интересный регион и перейти на заинтересовавшие посты!'
+ ThemeSwitchDescription: 'Позволяет поменять тему сайта -- есть 2 светлые и 2 тёмные темы!'
+ Hymn1: 'Приветствуем Вас на Саясайте!'
+ Hymn2: 'Мы Вас столько ждали!'
+ Hymn3: 'Столько радости от Вас на Саясайте!'
+ Hymn4: 'Где бы Вы не были, знайте, -- наша любовь крепка!'
diff --git a/main.go b/main.go
index 2f8ef4b..20e90f4 100644
--- a/main.go
+++ b/main.go
@@ -11,13 +11,14 @@ import (
"github.com/SayaAndy/saya-today-web/config"
"github.com/SayaAndy/saya-today-web/internal/b2"
+ "github.com/SayaAndy/saya-today-web/internal/factgiver"
"github.com/SayaAndy/saya-today-web/internal/lightgallery"
"github.com/SayaAndy/saya-today-web/internal/router"
"github.com/SayaAndy/saya-today-web/internal/tailwind"
"github.com/SayaAndy/saya-today-web/locale"
"github.com/dgraph-io/ristretto/v2"
"github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/redirect"
+ "github.com/gofiber/fiber/v2/middleware/cors"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/sqlite3"
"github.com/yuin/goldmark"
@@ -107,12 +108,10 @@ func main() {
ProxyHeader: "X-Forwarded-For",
})
- app.Use(redirect.New(redirect.Config{
- Rules: map[string]string{
- "/ru": "/",
- "/en": "/",
- },
- StatusCode: 301,
+ app.Use(cors.New(cors.Config{
+ AllowOrigins: "https://f003.backblazeb2.com",
+ AllowMethods: "GET,POST,OPTIONS",
+ AllowHeaders: "Origin, Content-Type, Accept",
}))
router.CCache, err = router.NewClientCache(db, []byte(cfg.Auth.Salt))
@@ -131,7 +130,14 @@ func main() {
os.Exit(1)
}
+ router.FactGiver, err = factgiver.NewFactGiver(&cfg.FactGiver, availableLanguages)
+ if err != nil {
+ slog.Error("fail to initialize fact giver", slog.String("error", err.Error()))
+ os.Exit(1)
+ }
+
app.Get("/", router.Root(cfg.AvailableLanguages))
+ app.Get("/:lang<len(2)>", router.Api_V1_GeneralPage(localization, availableLanguages))
app.Get("/:lang/map", router.Lang_Map(localization, availableLanguages, b2Client))
app.Get("/:lang/blog", router.Api_V1_GeneralPage(localization, availableLanguages))
app.Get("/:lang/blog/:title", router.Api_V1_GeneralPage(localization, availableLanguages))
diff --git a/static/input.css b/static/input.css
index 261aeb3..739bea7 100644
--- a/static/input.css
+++ b/static/input.css
@@ -10,6 +10,7 @@
--main-medium-color: #8a9d8a;
--main-light-color: #a5b8a5;
--background-dark-color: #ffffdd;
+ --background-medium-color: var(--background-dark-color);
--background-light-color: #fffff0;
--sidebar-text-color: var(--background-dark-color);
--sidebar-stroke-color: var(--main-dark-color);
@@ -31,6 +32,7 @@
--main-medium-color: #9bb665;
--main-light-color: #bbd095;
--background-dark-color: #f5eee2;
+ --background-medium-color: var(--background-dark-color);
--background-light-color: #fff9ef;
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-dark-color);
@@ -52,6 +54,7 @@
--main-medium-color: var(--color-sky-200);
--main-light-color: var(--color-sky-300);
--background-dark-color: #13131b;
+ --background-medium-color: var(--background-dark-color);
--background-light-color: #222230;
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-light-color);
@@ -73,6 +76,7 @@
--main-medium-color: #f6e4c5;
--main-light-color: #e8d4b8;
--background-dark-color: #413b34;
+ --background-medium-color: #62584f;
--background-light-color: #6d6258;
--sidebar-text-color: var(--main-dark-color);
--sidebar-stroke-color: var(--background-light-color);
@@ -615,6 +619,22 @@ body[data-theme~="ram"] .leaflet-tile {
}
}
+@media (min-aspect-ratio: 0.6/1) {
+ .ar-gt-0\.6\:flex-3\/5 {
+ flex: 60%;
+ }
+
+ .ar-gt-0\.6\:flex-2\/5 {
+ flex: 40%;
+ }
+}
+
+@media (max-aspect-ratio: 0.6/1) {
+ .ar-lt-0\.6\:flex-col {
+ flex-direction: column;
+ }
+}
+
@media (max-aspect-ratio: 0.4/1) {
.ar-lt-0\.4\:w-\[100vw\] {
width: 100vw;
@@ -625,6 +645,14 @@ body[data-theme~="ram"] .leaflet-tile {
}
}
+.home-page-heart {
+ position: absolute;
+ color: var(--color-pink-700);
+ user-select: none;
+ pointer-events: none;
+ font-family: serif;
+}
+
.night-star {
color: var(--color-yellow-100);
animation-name: blink;
diff --git a/views/index.html b/views/index.html
index 2b8c937..b52df94 100644
--- a/views/index.html
+++ b/views/index.html
@@ -106,7 +106,7 @@
<p class="font-patua mx-auto text-[2.5vmax] mt-[0.8vmin] mb-[0.8vmin]">saya.today</p>
<div class="menu-inner-body grow bg-background-light flex flex-adaptive mx-auto opacity-100">
{{- range .AvailableLanguages }}
- <img onclick="location.href='/{{ .Name }}/blog';" class="object-scale-down cursor-pointer flex-1 rounded-[20%] language-icon mx-auto my-auto relative" src="{{ .Flag }}" alt="{{ .Alt }}">
+ <img onclick="location.href='/{{ .Name }}';" class="object-scale-down cursor-pointer flex-1 rounded-[20%] language-icon mx-auto my-auto relative" src="{{ .Flag }}" alt="{{ .Alt }}">
{{- end }}
</div>
<p class="font-spectral mx-auto text-[1.5vmax] text-secondary mt-[0.8vmin] mb-[0.8vmin]">Choose your language</p>
diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html
index cb85a09..60608f4 100644
--- a/views/layouts/general-page.html
+++ b/views/layouts/general-page.html
@@ -7,9 +7,9 @@
<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">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/2.8.3/css/lightgallery.min.css">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/2.8.3/css/lg-zoom.min.css">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/2.8.3/css/lg-thumbnail.min.css">
+ <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/lightgallery/2.8.3/css/lightgallery.min.css">
+ <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/lightgallery/2.8.3/css/lg-zoom.min.css">
+ <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/lightgallery/2.8.3/css/lg-thumbnail.min.css">
<link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css">
<link href="/output.css" rel="stylesheet">
</head>
@@ -166,7 +166,7 @@
saya.today
</div>
<div class="text-[3vw] ar-lt-1.1:text-[5vh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1vw] flex flex-col ar-lt-1.1:flex-row gap-0 relative z-20">
- <i onclick="location.href='/{{ .Lang }}';" class="fas fa-home w-[5vw] ar-lt-1.1:w-[8vh] h-[5vw] ar-lt-1.1:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
+ <i onclick="changeUrl('/{{ .Lang }}');" class="fas fa-home w-[5vw] ar-lt-1.1:w-[8vh] h-[5vw] ar-lt-1.1:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
<i onclick="changeUrl('/{{ .Lang }}/blog');" class="fas fa-search w-[5vw] ar-lt-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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-1.1:w-[8vh] h-[5vw] ar-lt-1.1:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html
index b121cc6..863b417 100644
--- a/views/pages/blog-page.html
+++ b/views/pages/blog-page.html
@@ -1,7 +1,7 @@
{{ define "top-embeds" }}
<script>
- var galleryMap = new Map();
- var map;
+ let galleryMap = new Map();
+ let map;
window.addEventListener('popout', (e) => {
map.remove();
@@ -9,6 +9,8 @@
g.destroy();
});
galleryMap.clear();
+ map = null;
+ galleryMap = null;
}, {once: true});
</script>
{{ end }}
@@ -87,7 +89,8 @@
window.addEventListener('resize', resizeCb);
document.addEventListener('popout', (e) => {
- window.removeEventListener('resize', resizeCb, {once: true});
+ window.removeEventListener('resize', resizeCb);
+ galleryResizeTimeout = null;
});
</script>
{{ end }} \ No newline at end of file
diff --git a/views/pages/global-map.html b/views/pages/global-map.html
index d6e7281..a844e06 100644
--- a/views/pages/global-map.html
+++ b/views/pages/global-map.html
@@ -45,7 +45,7 @@
saya.today
</div>
<div class="text-[3vw] ar-lt-1.1:text-[5vh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1vw] flex flex-col ar-lt-1.1:flex-row gap-0 relative z-20">
- <i onclick="location.href='/';" class="fas fa-home w-[5vw] ar-lt-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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 }}';" class="fas fa-home w-[5vw] ar-lt-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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-1.1:w-[8vh] h-[5vw] ar-lt-1.1: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-1.1:w-[8vh] h-[5vw] ar-lt-1.1:h-[8vh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 flex rounded-lg"></i>
diff --git a/views/pages/home-page.html b/views/pages/home-page.html
new file mode 100644
index 0000000..9775ac3
--- /dev/null
+++ b/views/pages/home-page.html
@@ -0,0 +1,99 @@
+{{ define "body" }}
+<div class="bg-background-light flex flex-row ar-lt-0.6:flex-col w-full h-full inset-shadow overflow-y-auto text-[1vmax]/[2] tracking-[0.04vmin]">
+ <div class="ar-gt-0.6:flex-3/5 justify-center content-start p-[5%]">
+ <div class="flex flex-col justify-start items-start bg-background-dark mb-[2vmax]">
+ <p class="text-[3vmax]/[2] tracking-[0.2vmin] uppercase w-full font-spectral italic text-left
+ border-l-[0.4vmax] border-solid border-(--background-dark-color)
+ bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.Hymn1 }}</p>
+ <hr class="w-full border-t-[0.2vmax] border-dotted border-main-dark mb-[0.8vmin]">
+ <p class="text-[2vmax]/[2] -indent-[2vmax] ml-[2vmax] p-[0.4vmax]">{{ .L.HomePage.Hymn2 }}</p>
+ <p class="text-[2vmax]/[2] -indent-[2vmax] ml-[2vmax] p-[0.4vmax]">{{ .L.HomePage.Hymn3 }}</p>
+ <p class="text-[2vmax]/[2] -indent-[2vmax] ml-[2vmax] p-[0.4vmax]">{{ .L.HomePage.Hymn4 }}</p>
+ </div>
+ <div class="flex flex-col justify-center items-center">
+ <p class="text-[2vmax] w-full font-spectral italic text-left
+ border-l-[0.4vmax] border-solid border-(--background-dark-color)
+ bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.DidYouKnowThat }}</p>
+ <hr class="w-full border-t-[0.2vmax] border-dotted border-main-dark mb-[0.8vmin]">
+ <ul class="w-full text-left list-[circle]">
+ <li class="ml-[2vmax] mb-[0.8vmin]">{{ index .FunFacts 0 }}</li>
+ <li class="ml-[2vmax] mb-[0.8vmin]">{{ index .FunFacts 1 }}</li>
+ <li class="ml-[2vmax]">{{ index .FunFacts 2 }}</li>
+ </ul>
+ </div>
+ </div>
+
+ <div class="ar-gt-0.6:flex-2/5 justify-center content-start p-[5%]">
+ <div class="flex relative aspect-square justify-center content-center mb-[2vmax]">
+ {{- range $_ := iterate .FilledHeartCount }}
+ <div class="home-page-heart z-50">♥</div>
+ {{- end }}
+ {{- range $_ := iterate .OutlineHeartCount }}
+ <div class="home-page-heart z-50">♡</div>
+ {{- end }}
+ <img src="https://f003.backblazeb2.com/file/sayana-static/home-page-gifs/{{ .GifName }}"
+ class="home-page-gif z-49 -rotate-12 max-w-[70%] max-h-[70%] border-[1vmin] border-(--background-dark-color) border-inset">
+ </div>
+
+ <script>
+ function shuffleHearts() {
+ vmin = calculateVmin(1);
+ document.querySelectorAll('.home-page-heart').forEach((e) => {
+ let top, left;
+ do {
+ top = Math.floor(rand() * 90) + 5;
+ left = Math.floor(rand() * 90) + 5;
+ } while (top > 30 && top < 70 && left > 30 && left < 70);
+ const opacity = rand() * 0.7 + 0.2;
+ const size = rand() * 3.2 + 0.8;
+ e.style["top"] = `calc(${top}% - ${size}vmin / 2)`;
+ e.style["left"] = `calc(${left}% - ${size}vmin / 2)`;
+ e.style["opacity"] = `${opacity}`;
+ e.style["font-size"] = `${size}vmin`;
+ });
+ }
+
+ let shuffleHeartsResizeTimeout;
+ function shuffleHeartsResizeCb() {
+ clearTimeout(shuffleHeartsResizeTimeout);
+ shuffleHeartsResizeTimeout = setTimeout(shuffleHearts, 300);
+ }
+ window.addEventListener('resize', shuffleHeartsResizeCb);
+
+ document.addEventListener('popout', (e) => {
+ window.removeEventListener('resize', shuffleHeartsResizeCb);
+ shuffleHeartsResizeCb = null;
+ });
+ </script>
+
+ <script>
+ document.addEventListener('htmx:afterRequest', (e) => {
+ if (e.detail.xhr.status == 404 || e.detail.successful != true) {
+ return console.error(e);
+ }
+ if (e.detail.target.id == 'general-page-body') {
+ shuffleHearts();
+ }
+ }, {once: true});
+ </script>
+
+ <div class="flex flex-col justify-center items-center">
+ <p class="text-[2vmax] w-full font-spectral italic text-left
+ border-l-[0.4vmax] border-solid border-(--background-dark-color)
+ bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.SidebarDescription }}</p>
+ <hr class="w-full border-t-[0.2vmax] border-dotted border-main-dark mb-[0.8vmin]">
+ <div class="w-full grid grid-cols-[1fr_4fr] content-center items-center
+ [&>*:nth-child(4n+2)]:bg-(--background-medium-color) [&>*:nth-child(4n+3)]:bg-(--background-medium-color)" style="container-type: inline-size;">
+ <i class="fas fa-home w-full h-full text-[10cqi] content-center text-center text-main-dark"></i>
+ <p class="p-[0.4vmax]">{{ .L.HomePage.HomePageDescription }}</p>
+ <i class="fas fa-search w-full h-full text-[10cqi] content-center text-center text-main-dark"></i>
+ <p class="p-[0.4vmax]">{{ .L.HomePage.BlogSearchDescription }}</p>
+ <i class="fas fa-map w-full h-full text-[10cqi] content-center text-center text-main-dark"></i>
+ <p class="p-[0.4vmax]">{{ .L.HomePage.MarkerMapDescription }}</p>
+ <i class="fas fa-swatchbook w-full h-full text-[10cqi] content-center text-center text-main-dark"></i>
+ <p class="p-[0.4vmax]">{{ .L.HomePage.ThemeSwitchDescription }}</p>
+ </div>
+ </div>
+ </div>
+</div>
+{{ end }}