diff options
| author | 2025-09-06 19:18:00 +0700 | |
|---|---|---|
| committer | 2025-09-06 19:18:00 +0700 | |
| commit | 13a66428533c5f870901343dd505bcb835015430 (patch) | |
| tree | 5c63fabb02fa9c55c9e7cf2c26c2a35a90806f35 | |
| parent | 0802dc3a8835bc19cf994f9eb2b462df604d4262 (diff) | |
| download | web-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.go | 6 | ||||
| -rw-r--r-- | config/config.local.yaml | 10 | ||||
| -rw-r--r-- | config/config.prod.yaml | 10 | ||||
| -rw-r--r-- | config/config.stage.yaml | 10 | ||||
| -rw-r--r-- | internal/factgiver/factgiver.go | 67 | ||||
| -rw-r--r-- | internal/frontmatter/parser.go | 2 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-body.go | 13 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-bottom-embeds.go | 4 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-footer.go | 4 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-header.go | 5 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-top-embeds.go | 4 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page.go | 2 | ||||
| -rw-r--r-- | internal/templatemanager/templatemanager.go | 19 | ||||
| -rw-r--r-- | locale/localization.en.yaml | 12 | ||||
| -rw-r--r-- | locale/localization.go | 15 | ||||
| -rw-r--r-- | locale/localization.ru.yaml | 12 | ||||
| -rw-r--r-- | main.go | 20 | ||||
| -rw-r--r-- | static/input.css | 28 | ||||
| -rw-r--r-- | views/index.html | 2 | ||||
| -rw-r--r-- | views/layouts/general-page.html | 8 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 9 | ||||
| -rw-r--r-- | views/pages/global-map.html | 2 | ||||
| -rw-r--r-- | views/pages/home-page.html | 99 |
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: 'Где бы Вы не были, знайте, -- наша любовь крепка!' @@ -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 }} |