Diffstat (limited to 'views')
| -rw-r--r-- | views/layouts/general-page.html | 86 | ||||
| -rw-r--r-- | views/messages/new-post.html | 6 | ||||
| -rw-r--r-- | views/messages/verify-email.html | 10 | ||||
| -rw-r--r-- | views/pages/blog-catalogue.html | 55 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 72 | ||||
| -rw-r--r-- | views/pages/global-map.html | 125 | ||||
| -rw-r--r-- | views/pages/home-page.html | 45 | ||||
| -rw-r--r-- | views/pages/language-pick.html | 4 | ||||
| -rw-r--r-- | views/pages/unsubscribe-page.html | 2 | ||||
| -rw-r--r-- | views/pages/user-page.html | 46 | ||||
| -rw-r--r-- | views/partials/blog-page-like-button.html | 6 | ||||
| -rw-r--r-- | views/partials/catalogue-blog-cards.html | 10 | ||||
| -rw-r--r-- | views/partials/general-page-header.html | 4 | ||||
| -rw-r--r-- | views/partials/global-map-widget.html | 109 | ||||
| -rw-r--r-- | views/partials/personal-page-status.html | 2 |
15 files changed, 274 insertions, 308 deletions
diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index 3392ff7..06ef625 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -14,7 +14,7 @@ {{end}} /> {{- if eq .Property "og:title" }} - <title>{{.Content}} // SAYA.UZ</title> + <title>{{.Content}}</title> {{- end }} {{- if eq .Property "og:description" }} <meta name="description" content="{{.Content}}" /> {{- end }} {{- if eq .Property "og:url" }} @@ -22,15 +22,23 @@ {{- end }} {{- end }} <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/fonts/fonts.css" + href="{{ .StaticStorage.BaseUrl }}/fonts/fonts.css" /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet/1.9.4/leaflet.css" /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/libs/glightbox/3.3.0/css/glightbox.min.css" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorage.BaseUrl }}/libs/glightbox/3.3.0/css/glightbox.min.css" /> <link rel="stylesheet" href="/output.css" /> <link @@ -55,6 +63,11 @@ {{ . }} </script> {{- end }} + <script src="{{ .StaticStorage.BaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/protomaps-leaflet/5.0.0/dist/protomaps-leaflet.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/glightbox/3.3.0/js/glightbox.min.js"></script> </head> <body @@ -160,8 +173,6 @@ } </script> - <script src="{{ .StaticStorageBaseUrl }}/libs/glightbox/3.3.0/js/glightbox.min.js"></script> - <script> function sfc32(a, b, c, d) { return function () { @@ -202,18 +213,8 @@ var seed = cyrb128("{{ .Title }}"); var rand = sfc32(seed[0], seed[1], seed[2], seed[3]); - function calculateVmin(percent) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - - const smallerDimension = Math.min( - viewportWidth, - viewportHeight, - ); - - const vminValue = (smallerDimension / 100) * percent; - - return vminValue; + function calculateRem(remNum) { + return remNum * parseFloat(getComputedStyle(document.documentElement).fontSize); } const _probe = document.createElement('div'); @@ -308,7 +309,7 @@ theme = document.body.getAttribute("data-theme"); } - vmin = calculateVmin(1); + rem = calculateRem(1); var clWidth = document.documentElement.clientWidth; var clHeight = document.documentElement.clientHeight; @@ -327,11 +328,11 @@ "z-0", "absolute", ); - const x = Math.floor(rand() * clWidth); - const y = Math.floor(rand() * clHeight * 0.8); const rot = rand() * 90; const animDuration = rand() * 10 + 3; const sz = Math.floor(rand() * 8) + 12; + const x = Math.floor(rand() * clWidth - 1.42 * sz); // 1.42 ~ sqrt(2) + const y = Math.floor(rand() * clHeight * 0.8); star.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; star.style.animationDuration = `${animDuration}s`; star.style.fontSize = `${sz}px`; @@ -342,7 +343,7 @@ const frameCenters = new Array(4); for (i = 1; i <= 4; i++) { let frame = document.createElement("img"); - frame.src = `{{ .StaticStorageBaseUrl }}/themes/ram/ram-frame${i}.webp`; + frame.src = `{{ .StaticStorage.BaseUrl }}/themes/ram/ram-frame${i}.webp`; frame.classList.add( "ram-frame", "theme-decor", @@ -351,14 +352,14 @@ ); let x, y; outerLoop: while (true) { - x = Math.floor(rand() * clWidth - 20 * vmin); - y = Math.floor(rand() * clHeight - 20 * vmin); + x = Math.floor(rand() * clWidth - 22.64 * rem) + 11.32 * rem; // 11.32 ~ 8 * sqrt(2) + y = Math.floor(rand() * clHeight - 22.64 * rem) + 11.32 * rem; for (j = 0; j < i - 1; j++) { const xd = frameCenters[j].x - x; const yd = frameCenters[j].y - y; if ( Math.sqrt(xd * xd + yd * yd) < - 30 * vmin + 23 * rem ) { continue outerLoop; } @@ -401,6 +402,7 @@ localStorage.setItem("theme", theme); document.body.setAttribute("data-theme", theme); switchThemeDecor(theme); + switchColorScheme(matchMedia('(prefers-color-scheme: dark)').matches); } const savedTheme = localStorage.getItem("theme") || "lilac"; @@ -419,10 +421,9 @@ mq.addEventListener('change', (e) => switchColorScheme(e.matches)); </script> - <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/packery/3.0.0/dist/packery.pkgd.min.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/packery/3.0.0/dist/packery.pkgd.min.js"></script> <script> // Enable fitWidth-like attribute for Packery. Source: https://codepen.io/desandro/pen/kVmLYz @@ -627,15 +628,15 @@ class="flex flex-1 min-h-0 z-1 bg-paper bg-background shadow-elevation-3 @container/body" > <div - class="bg-paper bg-background-dark border-r-2 border-dashed border-main-hard" + class="bg-paper bg-background-dark border-r-[0.25rem] border-dashed border-main-hard" ></div> <div - class="max-h-full max-w-full flex flex-col grow bg-paper bg-background-dark relative pt-4 md:pr-4" + class="max-h-full max-w-full flex flex-col grow bg-paper bg-background-dark relative md:pr-4" > <header id="general-page-header" - class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col ml-8 basis-8" + class="max-xs:hidden [@media(max-height:32rem)]:hidden pt-4 flex flex-col ml-8 basis-8" >{{ .RenderedHeader }}</header> <div id="general-page-header-trigger" @@ -646,12 +647,12 @@ ></div> <hr - class="max-xs:hidden [@media(max-height:32rem)]:hidden border-t-4 border-dotted border-main-hard mb-2" + class="max-xs:hidden [@media(max-height:32rem)]:hidden border-t-[0.5rem] border-dotted border-main-hard mb-2" /> <main id="general-page-body" - class="relative flex-1 overflow-y-auto @container-size" + class="relative flex-1 overflow-y-auto @container-size inset-shadow-elevation-8" >{{ .RenderedBody }}</main> <div id="general-page-body-trigger" @@ -673,14 +674,10 @@ hx-target="#general-page-footer" hx-swap="innerHTML" ></div> - - <hr - class="border-t-4 border-dotted border-main-hard mb-2" - /> </div> <div - class="bg-paper bg-background-dark shadow-2xl border-l-2 border-dotted border-main-hard" + class="bg-paper bg-background-dark shadow-2xl border-l-[0.25rem] border-dotted border-main-hard" ></div> </div> </div> @@ -707,17 +704,6 @@ } }); - 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 sliceAngle = 360 / themeIcons.length; diff --git a/views/messages/new-post.html b/views/messages/new-post.html index 98f0b52..4e586e2 100644 --- a/views/messages/new-post.html +++ b/views/messages/new-post.html @@ -1,15 +1,15 @@ {{ define "body" }} -<p>{{ .L.Mail.NewPost.Intro }}</p> +<p>{{ l $.Lang "Mail" "NewPost" "Intro" }}</p> <table> <tr> - <td rowspan="3"><a href="https://{{ .ClientHost }}/{{ .Lang }}/blog/{{ .Post.FileName }}"><img src="{{ printf .ThumbnailBaseUrl .Post.Metadata.Thumbnail }}"></a></td> + <td rowspan="3"><a href="https://{{ .ClientHost }}/{{ .Lang }}/blog/{{ .Post.FileName }}"><img src="{{ printf .PhotoStorage.Thumbnail320p.BaseUrl .Post.Metadata.Thumbnail }}"></a></td> <td class="darkened" style="font-size: 24px"><a style="color: #273de1 !important;" href="https://{{ .ClientHost }}/{{ .Lang }}/blog/{{ .Post.FileName }}">{{ .Post.Metadata.Title }}</a></td> </tr> <tr> <td class="darkened" style="font-size: 16px">{{ .Post.Metadata.ShortDescription }}</td> </tr> <tr> - <td class="darkened" style="font-size: 16px">{{ .L.Mail.NewPost.CapturedOn }} {{ .Post.Metadata.ActionDate }}</td> + <td class="darkened" style="font-size: 16px">{{ l $.Lang "Mail" "NewPost" "CapturedOn" }} {{ .Post.Metadata.ActionDate }}</td> </tr> </table> {{ end }} diff --git a/views/messages/verify-email.html b/views/messages/verify-email.html index dd6ef33..e6fff76 100644 --- a/views/messages/verify-email.html +++ b/views/messages/verify-email.html @@ -1,10 +1,10 @@ {{ define "body" }} -<p>{{ .L.Mail.VerifyEmail.Welcome }}</p> -<p>{{ .L.Mail.VerifyEmail.Intro }}</p> +<p>{{ l $.Lang "Mail" "VerifyEmail" "Welcome" }}</p> +<p>{{ l $.Lang "Mail" "VerifyEmail" "Intro" }}</p> <ol> - <li>{{ .L.Mail.VerifyEmail.GotoLink }} <a href="https://{{ .ClientHost }}/{{ .Lang }}/user?email_code={{ .VerificationCode }}" class="outlier">>>></a></li> - <li>{{ .L.Mail.VerifyEmail.InputCode }} <span class="outlier">{{ .VerificationCode }}</span><span></li> + <li>{{ l $.Lang "Mail" "VerifyEmail" "GotoLink" }} <a href="https://{{ .ClientHost }}/{{ .Lang }}/user?email_code={{ .VerificationCode }}" class="outlier">>>></a></li> + <li>{{ l $.Lang "Mail" "VerifyEmail" "InputCode" }} <span class="outlier">{{ .VerificationCode }}</span><span></li> </ol> -<p>{{ .L.Mail.VerifyEmail.IfRandom }}</p> +<p>{{ l $.Lang "Mail" "VerifyEmail" "IfRandom" }}</p> <a href="https://{{ .ClientHost }}" class="outlier">{{ .ClientHost }}</a> {{ end }}
\ No newline at end of file diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 97917d6..ca8f578 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -45,63 +45,63 @@ </symbol> </svg> <div class="flex md:flex-row flex-col text-base h-full"> - <div class="relative flex shrink-0 w-full max-h-[30%] md:min-w-1/10 md:max-w-1/4 md:max-h-full md:h-full md:w-fit"> - <div class="absolute w-full h-full inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> + <div class="relative flex shrink-0 w-full max-h-[30%] md:min-w-1/10 md:max-w-1/4 md:max-h-full md:h-full md:w-fit shadow-elevation-6"> + <div class="absolute w-full h-full pointer-events-none z-20"></div> <form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML" - class="tags-list relative flex flex-col w-full bg-paper bg-background-light"> - <div class="flex flex-col overflow-y-auto"> + class="tags-list relative flex flex-col w-full"> + <div class="flex flex-col bg-paper bg-background-light overflow-y-auto inset-shadow-elevation-6"> <fieldset> - <legend class="font-bold w-full text-center">{{ .L.BlogSearch.OrderByHeader }}</legend> + <legend class="font-bold w-full text-center">{{ l $.Lang "BlogSearch" "OrderByHeader" }}</legend> <div class="md:flex flex-col grid grid-cols-3 grid-rows-2 grid-flow-col"> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> - {{ .L.BlogSearch.TitleOrdered }} + {{ l $.Lang "BlogSearch" "TitleOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-letter-asc"/></svg> </label> </div> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> - {{ .L.BlogSearch.TitleOrdered }} + {{ l $.Lang "BlogSearch" "TitleOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-letter-desc"/></svg> </label> </div> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> - {{ .L.BlogSearch.ActionDateOrdered }} + {{ l $.Lang "BlogSearch" "ActionDateOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-asc"/></svg> </label> </div> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> - {{ .L.BlogSearch.ActionDateOrdered }} + {{ l $.Lang "BlogSearch" "ActionDateOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-desc"/></svg> </label> </div> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> - {{ .L.BlogSearch.PublicationDateOrdered }} + {{ l $.Lang "BlogSearch" "PublicationDateOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-asc"/></svg> </label> </div> - <div> + <div class="m-1"> <label class="flex justify-start gap-1 items-center"> <input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> - {{ .L.BlogSearch.PublicationDateOrdered }} + {{ l $.Lang "BlogSearch" "PublicationDateOrdered" }} <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-desc"/></svg> </label> </div> </div> </fieldset> <fieldset class="mt-1"> - <legend class="font-bold w-full text-center">{{ .L.BlogSearch.TagsHeader }}</legend> + <legend class="font-bold w-full text-center">{{ l $.Lang "BlogSearch" "TagsHeader" }}</legend> <div class="md:flex flex-col grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-5 grid-flow-row-dense"> <div class="m-1"> - <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();" class="bg-accent-light rounded border-transparent text-accent-deep focus:border-transparent focus:bg-background-dark focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep"> {{ .L.BlogSearch.ChooseAllTags }}</label> + <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();" class="bg-accent-light rounded border-transparent text-accent-deep focus:border-transparent focus:bg-background-dark focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep"> {{ l $.Lang "BlogSearch" "ChooseAllTags" }}</label> </div> {{- range .Tags }} <div class="m-1"> @@ -111,23 +111,20 @@ </div> </fieldset> </div> - <hr class="border-t-2 border-dotted border-main-hard my-0.5"> - <div class="flex flex-row bg-paper bg-background-dark p-0.5 rounded-1 h-12"> - <button type="button" onclick="defaultSearchParams(); cleanHrefParams();" class="flex flex-1 justify-center text-main-hard bg-paper bg-accent-light cursor-pointer hover:bg-accent-deep active:text-background-dark transition-colors duration-500"> - <svg viewBox="0 0 24 24"><use href="#icon-camera-settings-flip"/></svg> + <hr class="border-t-[0.375rem] border-dotted border-main-hard my-2 mx-auto w-24"> + <div class="flex flex-row p-0.5 rounded-1 h-12"> + <button type="button" onclick="defaultSearchParams(); cleanHrefParams();" class="accent-button grow h-full"> + <svg class="h-full mx-auto" viewBox="0 0 24 24"><use href="#icon-camera-settings-flip"/></svg> </button> - <div class="w-1 bg-paper bg-background-light grow-0"></div> - <button type="submit" onclick="setHrefParams();" class="flex flex-1 justify-center text-main-hard bg-paper bg-accent-light cursor-pointer hover:bg-accent-deep active:text-background-dark transition-colors duration-500"> - <svg viewBox="0 0 24 24"><use href="#icon-search-magnifier"/></svg> + <button type="submit" onclick="setHrefParams();" class="accent-button grow h-full"> + <svg class="h-full mx-auto" viewBox="0 0 24 24"><use href="#icon-search-magnifier"/></svg> </button> </div> </form> </div> - <div class="w-[0.5dvh] border-r-2 border-dotted border-main-hard bg-paper bg-background-dark"></div> - <div class="relative flex grow h-full"> - <div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> + <div class="absolute w-full h-full inset-0 inset-shadow-elevation-6 pointer-events-none z-20"></div> <div hx-get="/api/v1/blog-search" hx-vals='js:{lang: "{{ .Lang }}", tz: clientTimeZone, highlight: "{{ .PreviousBlogPage }}"}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list" class="blog-cards relative grow flex flex-col overflow-y-auto bg-paper bg-background-light pr-2"> Loading... diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index a14d85c..3d3c623 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -1,33 +1,31 @@ {{ define "body" }} <input class="htmx-exit-trigger" type="hidden" name="codename" value="{{ .Codename }}"> -<div class="sticky top-0 left-0 w-full h-[100cqb] -mb-[100cqb] inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> -<article class="relative bg-paper bg-background-light flex flex-col px-8 py-4 overflow-y-auto"> - <div class="flex flex-col sm:flex-row h-[30cqb] sm:h-[15cqb]"> - <div class="grow shrink-2 flex-1 overflow-y-auto -ml-8 -mt-4 {{if .Medley}}max-sm:-mr-8{{else}}-mr-8{{end}} z-1 shadow-elevation-2"> - <div class="multitone min-h-full" style="--multitone-bg: url({{ printf $.ThumbnailBaseUrl .Thumbnail }});"> +<div class="relative bg-paper bg-background-light flex flex-col py-4 overflow-y-auto"> + <div class="flex flex-col sm:flex-row h-[30cqb] sm:h-[15cqb] shadow-elevation-6"> + <div class="grow shrink-2 flex-1 overflow-y-auto -mt-4 z-1 shadow-elevation-3"> + <div class="multitone min-h-full" style="--multitone-bg: url({{ printf $.PhotoStorage.Thumbnail560p.BaseUrl .Thumbnail }});"> <div class="ml-8 py-4"> <h1 class="max-xs:flex flex-row content-center [@media(max-height:32rem)]:block hidden font-gentium text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1"> <div hx-get="/api/v1/like" hx-target="this" hx-swap="outerHTML" hx-trigger="load"></div> {{ .Title }} </h1> <p class="block grow text-lg font-gentium text-secondary"> - <b>{{ .L.Metadata.Published }}</b>: + <b>{{ l $.Lang "Metadata" "Published" }}</b>: <time datetime="{{ .PublishedDate }}" hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> {{ .PublishedDate }} </time> </p> - <p class="block grow text-lg font-gentium text-secondary"><b>{{ .L.Metadata.Action }}</b>: {{ .ActionDate }}</p> + <p class="block grow text-lg font-gentium text-secondary"><b>{{ l $.Lang "Metadata" "Action" }}</b>: {{ .ActionDate }}</p> <p class="block grow text-md font-gentium font-thin text-main-hard">{{ .ShortDescription }}</p> </div> </div> </div> - <hr class="block sm:hidden border-t-3 bg-paper bg-background-dark border-dotted border-main-hard w-full"> + <hr class="block sm:hidden border-t-[0.1875rem] bg-paper bg-background-dark border-dotted border-main-hard w-full"> {{- if .Medley }} - <div class="overflow-y-auto grow-2 shrink-0 flex-1 bg-paper bg-background-dark shadow-elevation-2 -mr-8 sm:-mt-4 max-sm:-ml-8 z-2"> - <h2 class="text-2xl font-gentium font-extrabold text-main-hard tracking-[.0125rem] ml-2"> - {{ .Medley }} + <div class="overflow-y-auto grow-2 shrink-0 flex-1 bg-paper bg-background-dark shadow-elevation-3 sm:-mt-4 z-2"> + <h2 class="text-2xl font-gentium font-extrabold text-main-hard tracking-[.0125rem] ml-2 mb-2 border-b-[0.25rem] border-dotted w-fit"> + {{ l .Lang "Medleys" .Medley }} </h2> - <hr class="block border-t-2 my-1 border-dotted border-main-hard w-full"> <div hx-get="/api/v1/blog-search" hx-vals='js:{lang: "{{ .Lang }}", tz: clientTimeZone, medley: "{{ .Medley }}", sort: "medley", hideTags: true, hidePublishedTime: true, highlight: "{{ .Codename }}"}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" class="grow flex flex-col pr-2"> Loading... @@ -35,50 +33,18 @@ </div> {{- end }} </div> - <hr class="block border-t-3 mb-2 border-dotted border-main-hard w-full ml-auto mr-auto"> - {{ .ParsedMarkdown }} + <hr class="block border-t-[0.375rem] w-24 mx-auto my-4 border-dotted border-main-hard"> + <article class="px-8 flex flex-col"> + {{ .ParsedMarkdown }} + </article> {{- if .MapLocationX }} - <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> - <div id="map-container" class="relative p-1 flex-none ml-auto mr-auto w-[80%] lg:w-[60%] h-[30dvh] md:h-[40dvh]"></div> - <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> + <hr class="border-t-[0.375rem] border-dotted border-main-hard my-2 w-24 mx-auto"> + <div id="map-outer-container" class="relative p-1 flex-none mx-auto w-[80%] lg:w-[60%] h-[30dvh] md:h-[40dvh]" + hx-get="/api/v1/map" hx-vals='{"lang": "{{ .Lang }}", "codename": "{{ .Codename }}", "zoom": 8}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> + </div> + <hr class="border-t-[0.375rem] border-dotted border-main-hard my-2 w-24 mx-auto"> <script> - function initLocationMap(id, x, y, relativeErrorMeters = 0) { - map = L.map(id).setView([x, y], 8); - - L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.png', { - maxZoom: 20, - attribution: '© <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a>, © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>', - }).addTo(map); - - if (relativeErrorMeters != 0) { - var circle = L.circle([x, y], { - color: '#8a9d8a', - fillColor: '#8a9d8a', - fillOpacity: 0.15, - radius: relativeErrorMeters - }).addTo(map); - } - - var marker = L.marker([x, y], { - title: '{{ .Title }}', - }).addTo(map) - .bindPopup('<span><b>{{ .Title }}</b><br><a href="https://www.openstreetmap.org/#map=13/{{ .MapLocationX }}/{{ .MapLocationY }}">{{ .MapLocationX }}, {{ .MapLocationY }}</a></span>') - .openPopup(); - } - - if (map) { - map.remove(); - map = null; - } - - setTimeout(() => initLocationMap( - 'map-container', - {{ .MapLocationX }}, - {{ .MapLocationY }}, - {{ .MapLocationAreaMeters }} - ), 1000); - var lightbox = GLightbox({ selector: '.glightbox', moreLength: 0 diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 788eac9..4d1853f 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -3,23 +3,23 @@ <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <meta name="description" content="{{ .L.GlobalMap.Description }}" /> - <title>{{ .L.GlobalMap.Header }} // SAYA.UZ</title> - <meta property="og:title" content="{{ .L.GlobalMap.Header }}" /> + <meta name="description" content="{{ l $.Lang "GlobalMap" "Description" }}" /> + <title>{{ l $.Lang "GlobalMap" "Header" }} // SAYA.UZ</title> + <meta property="og:title" content="{{ l $.Lang "GlobalMap" "Header" }}" /> <meta property="og:description" - content="{{ .L.GlobalMap.Description }}" + content="{{ l $.Lang "GlobalMap" "Description" }}" /> <meta property="og:url" content="{{ .CanonicalEndpoint }}/{{ .Lang }}/map" /> <meta property="og:type" content="site" /> - <title>{{ .L.GlobalMap.Header }} // SAYA.UZ</title> + <title>{{ l $.Lang "GlobalMap" "Header" }} // SAYA.UZ</title> <link rel="canonical" href="{{ .CanonicalEndpoint }}/{{ .Lang }}/map" /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/fonts/fonts.css" + href="{{ .StaticStorage.BaseUrl }}/fonts/fonts.css" /> {{ block "header" . }}{{ end }} <link rel="stylesheet" href="/output.css" /> @@ -42,16 +42,20 @@ /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet/1.9.4/leaflet.css" /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.css" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.css" /> <link rel="stylesheet" - href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css" + href="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css" /> + <script src="{{ .StaticStorage.BaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/protomaps-leaflet/5.0.0/dist/protomaps-leaflet.js"></script> + <script src="{{ .StaticStorage.BaseUrl }}/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> </head> <body @@ -216,20 +220,14 @@ </div> </nav> - <div - id="map-container" - class="bg-paper bg-background-dark flex z-10 w-full h-full" - ></div> - - <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> + <div id="map-outer-container" class="bg-paper bg-background-dark flex z-10 w-full h-full" + hx-get="/api/v1/map" hx-vals='{"lang": "{{ .Lang }}", "codename": "{{ .Codename }}", "zoom": 4, "zoomPosition": "topright"}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> + </div> <script> const themeWheel = document.getElementById("theme-wheel"); const themeButton = document.getElementById("sidebar-theme-button"); const themeIcons = document.querySelectorAll(".theme-icon"); - const THUMB_BASE = "{{ .ThumbnailBaseUrl }}"; themeIcons.forEach((icon) => { if (icon.getAttribute("data-theme") == savedTheme) { @@ -250,6 +248,7 @@ function positionThemeIcons() { const sliceAngle = 360 / themeIcons.length; + const themeWheelRadius = convertRemToPixels(6); themeIcons.forEach((icon, index) => { const startAngle = index * sliceAngle; @@ -367,95 +366,5 @@ } }); </script> - - <script> - var map; - var markers = L.markerClusterGroup(); - - function createCustomIcon(color, borderColor = "var(--color-main-soft)") { - const svgIcon = ` - <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" - fill="${color}" - stroke="${borderColor}" - stroke-width="1" - stroke-linejoin="round"/> - </svg> - `; - - return L.divIcon({ - html: svgIcon, - className: 'custom-svg-marker', - iconSize: [32, 32], - iconAnchor: [16, 32], - popupAnchor: [0, -32] - }); - } - - function initLocationMap() { - map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); - - L.tileLayer('https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.png', { - maxZoom: 20, - attribution: '© <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a>, © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>', - zoomControl: false - }).addTo(map); - - L.control.zoom({ - position: 'topright' - }).addTo(map); - - map.addLayer(markers); - } - - function markerToHsl(str, newCoef) { - let hash = 0; - for (let i = 0; i < str.length / 2; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); - hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash); - hash %= 360; - } - - return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)]; - } - - function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1) { - const [h, s, l] = markerToHsl(title, newCoef); - const color = `hsl(${h} ${s}% ${l}%)`; - - if (relativeErrorMeters != 0) { - var circle = L.circle([x, y], { - color: color, - fillColor: color, - fillOpacity: 0.15, - radius: relativeErrorMeters - }).addTo(map); - } - - var marker = L.marker([x, y], { - icon: createCustomIcon(color), - title: title, - }).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-12 h-12 mr-1" src="${THUMB_BASE.replace('%s', thumbnail)}"> - <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> - </div>`); - - markers.addLayer(marker); - } - - document.addEventListener('DOMContentLoaded', () => { - initLocationMap(); - {{- range .MapMarkers }} - mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }}); - {{- end }} - }); - - window.addEventListener('resize', function() { - setTimeout(() => { - map.invalidateSize(); - }, 100); - }); - </script> </body> </html> diff --git a/views/pages/home-page.html b/views/pages/home-page.html index 6e026aa..d494a08 100644 --- a/views/pages/home-page.html +++ b/views/pages/home-page.html @@ -17,24 +17,23 @@ <path d="M4.59 15.35a39.94 39.94 0 0 0 -2.94 3.25 0.35 0.35 0 1 0 0.53 0.46c0.59 -0.6 1.69 -1.51 2.89 -2.58 0.63 -0.57 1.29 -1.17 1.9 -1.79s1.42 -1.52 2 -2.21 -0.14 -1 -0.82 -0.39c-0.52 0.46 -1.08 0.94 -1.67 1.47s-1.28 1.19 -1.89 1.79Z" fill="var(--color-accent-deep)" stroke-width="1"></path> </g></symbol> </svg> -<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> <div class="relative bg-paper bg-background-light font-gentium flex flex-col sm:flex-row w-full h-full overflow-y-auto text-base/loose tracking-wide z-10"> <div class="sm:flex-3/5 justify-center content-start p-8"> <div class="flex flex-col justify-start items-start bg-paper bg-background-dark mb-8 shadow-[0.4rem_0.4rem_0.4rem_black]"> <p class="text-3xl/loose sm:text-4xl/loose lg:text-5xl/loose tracking-widest uppercase w-full font-m-plus font-thin italic text-left - border-l-4 border-solid border-background-dark - bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.Hymn1 }}</p> - <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> - <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn2 }}</p> - <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn3 }}</p> - <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn4 }}</p> + border-l-[0.25rem] border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ l $.Lang "HomePage" "Hymn1" }}</p> + <hr class="w-full border-t-[0.125rem] border-dotted border-main-hard mb-1"> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ l $.Lang "HomePage" "Hymn2" }}</p> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ l $.Lang "HomePage" "Hymn3" }}</p> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ l $.Lang "HomePage" "Hymn4" }}</p> </div> <div class="flex flex-col justify-center items-center"> <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left - border-l-1.5 border-solid border-background-dark - bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.DidYouKnowThat }}</p> - <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> + border-l-[0.125rem] border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ l $.Lang "HomePage" "DidYouKnowThat" }}</p> + <hr class="w-full border-t-[0.125rem] border-dotted border-main-hard mb-1"> <ul class="w-full text-left list-[circle]"> <li class="ml-4 mb-1">{{ index .FunFacts 0 }}</li> <li class="ml-4 mb-1">{{ index .FunFacts 1 }}</li> @@ -46,9 +45,9 @@ <div class="sm:flex-2/5 justify-center content-start p-8"> <div class="flex flex-col justify-center items-start mb-4"> <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left - border-l-1.5 border-solid border-background-dark - bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.Contacts }}</p> - <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> + border-l-[0.125rem] border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ l $.Lang "HomePage" "Contacts" }}</p> + <hr class="w-full border-t-[0.125rem] border-dotted border-main-hard mb-1"> <a href="https://t.me/EarlInisMona"> <svg class="w-4 h-4 mr-1 inline" viewBox="0 0 24 24"><use href="#icon-telegram"/></svg> <span class="font-bold">Telegram:</span> @@ -74,7 +73,7 @@ <div class="home-page-heart z-50">♡</div> {{- end }} <img src="{{ .GifUrl }}" - class="home-page-gif z-49 -rotate-12 max-w-[70%] max-h-[70%] border-4 border-background-dark border-inset"> + class="home-page-gif z-49 -rotate-12 max-w-[70%] max-h-[70%] border-[0.25rem] border-background-dark border-inset"> </div> <script> @@ -121,23 +120,23 @@ <div class="flex flex-col justify-center items-center"> <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left - border-l-1.5 border-solid border-color-background-dark - bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.SidebarDescription }}</p> - <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> + border-l-[0.125rem] border-solid border-color-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ l $.Lang "HomePage" "SidebarDescription" }}</p> + <hr class="w-full border-t-[0.125rem] border-dotted border-main-hard mb-1"> <div class="w-full grid grid-cols-[1fr_4fr] content-center items-center [&>*:nth-child(4n+2)]:bg-background-medium/50 [&>*:nth-child(4n+3)]:bg-background-medium/50" style="container-type: inline-size;"> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-back"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.BackDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "BackDescription" }}</p> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-logo"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.HomePageDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "HomePageDescription" }}</p> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-newspaper"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.BlogSearchDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "BlogSearchDescription" }}</p> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-map"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.MarkerMapDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "MarkerMapDescription" }}</p> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-palette"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.ThemeSwitchDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "ThemeSwitchDescription" }}</p> <svg viewBox="0 0 24 24" class="h-full w-[15cqi] context-center text-main-hard"><use href="#icon-account"/></svg> - <p class="p-1 w-[85cqi]">{{ .L.HomePage.AccountDescription }}</p> + <p class="p-1 w-[85cqi]">{{ l $.Lang "HomePage" "AccountDescription" }}</p> </div> </div> </div> diff --git a/views/pages/language-pick.html b/views/pages/language-pick.html index f4119e4..9576a65 100644 --- a/views/pages/language-pick.html +++ b/views/pages/language-pick.html @@ -1,7 +1,7 @@ {{ define "body" }} <div class="flex flex-col justify-center text-3xl h-full"> {{- range .AvailableLanguages }} - <hr class="border-t-2 border-dotted border-main-hard"> + <hr class="border-t-[0.125rem] border-dotted border-main-hard"> <div class="nth-[2n+1_of_div]:bg-background-medium/50 nth-[2n_of_div]:bg-background-light/50 p-2.5 flex flex-row justify-center justify-items-center"> <a href="/{{ .Name }}" class="w-12 h-12 basis-12 shrink-0 mr-4"> <img onclick="return changeUrl('{{ .Name }}');" class="w-full h-full aspect-square cursor-pointer object-cover rounded-md select-none" src="{{ .Flag }}"> @@ -11,6 +11,6 @@ </a> </div> {{- end }} - <hr class="border-t-2 border-dotted border-main-hard"> + <hr class="border-t-[0.125rem] border-dotted border-main-hard"> </div> {{ end }} diff --git a/views/pages/unsubscribe-page.html b/views/pages/unsubscribe-page.html index 9a78488..12ff1f0 100644 --- a/views/pages/unsubscribe-page.html +++ b/views/pages/unsubscribe-page.html @@ -21,7 +21,7 @@ href="/favicon.ico" type="image/x-icon" /> - <title>{{ .L.UnsubscribePage.Header }} // SAYA.UZ</title> + <title>{{ l $.Lang "UnsubscribePage" "Header" }} // SAYA.UZ</title> <style> .bg-paper { background-image: url(data:image/webp;base64,UklGRi4BAABXRUJQVlA4ICIBAABQCQCdASqAAIAAPm02lUmkIqIkIImIgA2JaW3rMFmGDAOKLAoJtP591ByFR74Tun2DP+Lygpfsz5FH7+cKgxi2Islfd03i5AJdXUfU0SDc8SXYAAD++hlzeYAJWQEF8ZdkJdlcA+5btZg0FMGaso8SHoBph0qAOQysElJ5C5SJFNoKIV1J550UyL7Y9Gh0/c/uzLnBmr8lvz7k1WeA81H4S5ViU/wBqa7aM1m8D9rDtrk8cSTqx+xWF6MS8UfZGSsks6CT/igtgv6syFN9uNA3LjnjiCJKYOvFcMkX+jB2578GWVo3nyLaM1QNPYCxn3TiUbnf7U0MdD2NsBpg1+Jd1wu6yW/U+goN+IQS6Nt4RMWltVcZHlxrneKJi0SobO0AAA==); diff --git a/views/pages/user-page.html b/views/pages/user-page.html index 0dd006a..a94dcb3 100644 --- a/views/pages/user-page.html +++ b/views/pages/user-page.html @@ -1,74 +1,74 @@ {{ define "body" }} -<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> +<div class="absolute w-full h-full inset-0 inset-shadow-elevation-6 pointer-events-none z-20"></div> <div class="relative bg-paper bg-background-light flex flex-col px-8 py-4"> <form id="email-form" class="w-full mb-2 flex flex-col relative crossable" hx-get="/api/v1/email/is-in-verification" hx-target="#email-message" hx-swap="outerHTML" hx-trigger="load" hx-indicator="this"> <div class="flex flex-row w-full mb-4 z-21"> <div class="flex flex-col w-full sm:flex-3/5 grow-0"> <label class="block text-main-medium font-bold mb-1 z-22"> - {{ .L.UserProfile.EmailHeader }} + {{ l $.Lang "UserProfile" "EmailHeader" }} </label> - <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" - id="email-input" name="email" type="text" value="{{ .Email }}" placeholder="{{ .L.UserProfile.TypeEmail }}" maxlength="64" + <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-elevation-6 focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + id="email-input" name="email" type="text" value="{{ .Email }}" placeholder="{{ l $.Lang "UserProfile" "TypeEmail" }}" maxlength="64" data-validated-email="{{ .Email }}" onchange="checkIfValidated(this);"> </div> </div> <button class="w-max mx-auto bg-main-hard hover:bg-main-medium active:bg-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" hx-post="/api/v1/email/send-verification-code" hx-target="#email-message" hx-swap="outerHTML" hx-indicator="#email-form"> - {{ .L.UserProfile.SendCodeButton }} + {{ l $.Lang "UserProfile" "SendCodeButton" }} </button> </form> - <div id="email-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> + <div id="email-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-elevation-6 py-2 px-4 text-center"></div> - <hr class="my-2 border-t-4 border-dotted border-main-hard"> + <hr class="my-2 border-t-[0.25rem] border-dotted border-main-hard"> <form id="email-verification-form" class="w-full mb-2 flex flex-col relative hidden crossable" {{ if .EmailCode }}hx-post="/api/v1/email/verify" hx-target="#verification-message" hx-swap="outerHTML" hx-trigger="load" hx-indicator="this"{{ end }}> <div class="flex flex-row w-full mb-4 z-21"> <div class="flex flex-col w-full sm:flex-2/5 grow-0"> <label class="block text-main-medium font-bold ml-1 z-22"> - {{ .L.UserProfile.VerificationCodeHeader }} + {{ l $.Lang "UserProfile" "VerificationCodeHeader" }} </label> - <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-elevation-6 focus:outline-none focus:bg-background-light focus:text-main-hard z-22" name="email_code" type="text" value="{{ .EmailCode }}" placeholder="0123456789ABCDEF" maxlength="16" autocapitalize="characters"> </div> </div> <button class="w-max mx-auto bg-main-hard hover:bg-main-medium active:bg-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" hx-post="/api/v1/email/verify" hx-target="#verification-message" hx-swap="outerHTML" hx-indicator="#email-verification-form"> - {{ .L.UserProfile.VerifyButton }} + {{ l $.Lang "UserProfile" "VerifyButton" }} </button> </form> - <div id="verification-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> + <div id="verification-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-elevation-6 py-2 px-4 text-center"></div> </div> -<hr class="my-4 border-t-4 border-dotted border-main-hard"> +<hr class="my-4 border-t-[0.25rem] border-dotted border-main-hard"> -<div class="bg-paper bg-background-light flex flex-col inset-shadow-[0_0_0.4rem_black] px-8 py-4"> +<div class="bg-paper bg-background-light flex flex-col inset-shadow-elevation-6 px-8 py-4"> <form id="subs-form" class="w-full mb-2 flex flex-col relative crossable"> <div class="flex flex-col w-full mb-4 z-21"> <label class="block text-main-medium font-bold mb-1 z-22"> - {{ .L.UserProfile.SubscriptionHeader }} + {{ l $.Lang "UserProfile" "SubscriptionHeader" }} </label> <div class="flex flex-col sm:flex-row w-full"> <div class="m-1 w-1/3"> - <label><input class="mr-1" type="radio" id="tagsNone" name="tags" value="none" {{ if eq .TagsPicked "none" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsNone }}</label> + <label><input class="mr-1" type="radio" id="tagsNone" name="tags" value="none" {{ if eq .TagsPicked "none" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ l $.Lang "UserProfile" "TagsNone" }}</label> </div> <div class="m-1 w-1/3"> - <label><input class="mr-1" type="radio" id="tagsAll" name="tags" value="all" {{ if eq .TagsPicked "all" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsAll }}</label> + <label><input class="mr-1" type="radio" id="tagsAll" name="tags" value="all" {{ if eq .TagsPicked "all" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ l $.Lang "UserProfile" "TagsAll" }}</label> </div> <div class="m-1 w-1/3"> - <label><input class="mr-1" type="radio" id="tagsSpecific" name="tags" value="specific" {{ if eq .TagsPicked "specific" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsSpecific }}</label> + <label><input class="mr-1" type="radio" id="tagsSpecific" name="tags" value="specific" {{ if eq .TagsPicked "specific" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ l $.Lang "UserProfile" "TagsSpecific" }}</label> </div> </div> <div id="pick-tags-form" class="flex flex-col w-full relative crossable"> - <input type="text" class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + <input type="text" class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-elevation-6 focus:outline-none focus:bg-background-light focus:text-main-hard z-22" list="existing-tags" onchange="addTag(this.value);" onkeydown="if (event.keyCode === 13) {addTag(this.value); this.value=''; return false;}"> <datalist id="existing-tags"> {{- range .ExistingTags }} <option value="{{ .Name }}"> {{- end }} </datalist> - <div id="tags-picked-list" class="bg-paper bg-background-dark flex flex-wrap inset-shadow-[0_0_0.4rem_black] p-2 m-4 w-full"> + <div id="tags-picked-list" class="bg-paper bg-background-dark flex flex-wrap inset-shadow-elevation-6 p-2 m-4 w-full"> {{- range .TagsPickedList }} <div id="tagPicked{{ . }}" class="bg-paper bg-main-hard text-background-light m-2 flex flex-row"> <p class="py-1 px-2">{{ . }}</p> @@ -80,11 +80,11 @@ </div> <button class="w-max mx-auto bg-color-main-hard hover:bg-main-medium active:bg-color-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" hx-put="/api/v1/subs" hx-target="#subs-message" hx-swap="outerHTML" hx-indicator="#subs-form"> - {{ .L.UserProfile.SaveButton }} + {{ l $.Lang "UserProfile" "SaveButton" }} </button> </div> </form> - <div id="subs-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> + <div id="subs-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-elevation-6 py-2 px-4 text-center"></div> </div> <script> @@ -168,7 +168,7 @@ function addTag(name) { found = [...existingTags].find(opt => opt.value===name); if (!found) { subsMessage.classList.remove("hidden"); - subsMessage.innerHTML = "{{ .L.UserProfile.TagDoesNotExist }}".replace("{}", name); + subsMessage.innerHTML = "{{ l $.Lang "UserProfile" "TagDoesNotExist" }}".replace("{}", name); return; } @@ -177,7 +177,7 @@ function addTag(name) { found = [...subscribedTags].find(opt => opt.value===name); if (found) { subsMessage.classList.remove("hidden"); - subsMessage.innerHTML = "{{ .L.UserProfile.TagAlreadyAdded }}".replace("{}", name); + subsMessage.innerHTML = "{{ l $.Lang "UserProfile" "TagAlreadyAdded" }}".replace("{}", name); return; } diff --git a/views/partials/blog-page-like-button.html b/views/partials/blog-page-like-button.html index af668e6..50ceae9 100644 --- a/views/partials/blog-page-like-button.html +++ b/views/partials/blog-page-like-button.html @@ -1,5 +1,5 @@ <button hx-put="/api/v1/like" hx-vals='{"like": {{ not .Liked }}}' hx-target="this" hx-swap="outerHTML" hx-trigger="click" - class="accent-button rounded-xl flex flex-row shrink-0 w-fit h-full mx-2 my-auto{{ if .Liked }} active{{ end }}"> + class="accent-button rounded-xl relative shrink-0 w-fit aspect-square mr-4 p-2 my-auto{{ if .Liked }} active{{ end }}"> {{ if .Liked }} <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="Like-Hand-1--Streamline-Freehand" class="h-10"> <desc> @@ -12,7 +12,7 @@ <path d="M13.94 4.33a2.57 2.57 0 0 1 1.43 0.88 4.52 4.52 0 0 1 1 1.54 0.57 0.57 0 0 0 0.56 0.37 0.57 0.57 0 0 0 0.53 -0.41 2.81 2.81 0 0 1 1 -1.41 6.9 6.9 0 0 1 1.69 -1 0.49 0.49 0 0 0 0.31 -0.43 0.5 0.5 0 0 0 -0.24 -0.47 8.18 8.18 0 0 1 -2.26 -1.47 2.39 2.39 0 0 1 -0.71 -1.46 0.34 0.34 0 0 0 -0.34 -0.34 0.35 0.35 0 0 0 -0.34 0.35 0.29 0.29 0 0 0 -0.27 0.23 4.41 4.41 0 0 1 -1 1.63 3.53 3.53 0 0 1 -1.48 1 0.47 0.47 0 0 0 -0.32 0.5 0.48 0.48 0 0 0 0.44 0.49Zm2 -1.47a5.53 5.53 0 0 0 0.82 -1.44 3.34 3.34 0 0 0 0.59 1 6.91 6.91 0 0 0 1.53 1.3 5.93 5.93 0 0 0 -1.48 1 6.91 6.91 0 0 0 -0.51 0.57 4.8 4.8 0 0 0 -0.7 -0.85 4 4 0 0 0 -1 -0.74 5.76 5.76 0 0 0 0.71 -0.84Z" fill="var(--color-accent-deep)" fill-rule="evenodd" stroke-width="1"></path> </g> </svg> - <div class="ml-auto bg-accent-light rounded-full w-6 h-6 text-main! font-m-plus! font-normal! tracking-normal! not-italic! text-lg!">{{ .LikedCount }}</div> + <div class="absolute left-10 -top-2 bg-accent-light rounded-full w-6 h-6 text-main! font-m-plus! font-normal! tracking-normal! not-italic! text-lg!">{{ .LikedCount }}</div> {{ else }} <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="Like-Hand--Streamline-Freehand" class="h-10"> <desc> @@ -23,6 +23,6 @@ <path d="M3.53 9.34a2.22 2.22 0 0 0 -1.24 -0.88 5.37 5.37 0 0 0 -2 -0.26 0.3 0.3 0 0 0 -0.29 0.31 0.3 0.3 0 0 0 0.32 0.29 4.36 4.36 0 0 1 1.44 0.25 1.74 1.74 0 0 1 1 0.69A9.16 9.16 0 0 1 3 11.8c0 0.64 0.06 1.38 0.09 2.14 0 1.07 0.08 2.2 0.11 3.22s0.06 2.19 0 2.84c0 0.23 -0.12 0.45 -0.07 0.51A2.7 2.7 0 0 1 2 21a3.34 3.34 0 0 1 -1.49 0 0.34 0.34 0 0 0 -0.22 0.65 4.22 4.22 0 0 0 1.82 0.13 3.37 3.37 0 0 0 1.79 -0.69 1.82 1.82 0 0 0 0.26 -0.82 37.13 37.13 0 0 0 0.14 -5.13c0 -1 -0.1 -2 -0.19 -2.83a12.48 12.48 0 0 0 -0.58 -2.97Z" fill="var(--color-accent-deep)" fill-rule="evenodd" stroke-width="1"></path> </g> </svg> - <div class="ml-auto bg-accent-deep rounded-full w-6 h-6 text-background! font-m-plus! font-normal! tracking-normal! not-italic! text-lg!">{{ .LikedCount }}</div> + <div class="absolute left-10 -top-2 bg-accent-deep rounded-full w-6 h-6 text-background! font-m-plus! font-normal! tracking-normal! not-italic! text-lg!">{{ .LikedCount }}</div> {{ end }} </button> diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html index 799955f..6466262 100644 --- a/views/partials/catalogue-blog-cards.html +++ b/views/partials/catalogue-blog-cards.html @@ -1,8 +1,8 @@ {{- range .BlogPages }} -<hr class="first-of-type:hidden border-t-2 border-dotted border-main-hard"> -<div {{ if .ToHighlight }}id="blog-page-highlighted"{{ end }} class="w-full flex flex-row items-stretch {{ if .ToHighlight }}bg-accent-light bg-paper{{ end }}"> - <div class="relative min-h-24 flex flex-col w-28 overflow-hidden shrink-0 mask-r-from-70% mask-r-to-100%"> - <img onclick="return changeUrl('{{ .ArticleLink }}');" class="h-full min-h-24 w-full cursor-pointer select-none object-cover block absolute" src="{{ printf $.ThumbnailBaseUrl .Thumbnail }}"> +<hr class="first-of-type:hidden border-t-[0.375rem] my-1 w-24 mx-auto border-dotted border-main-hard"> +<div {{ if .ToHighlight }}id="blog-page-highlighted"{{ end }} class="w-full flex flex-row items-stretch mask-t-from-[calc(100%-0.5rem)] mask-t-to-100% mask-b-from-[calc(100%-0.5rem)] mask-b-to-100% {{ if .ToHighlight }}bg-accent-light bg-paper{{ end }}"> + <div class="relative min-h-24 flex flex-col w-32 overflow-hidden shrink-0 mask-r-from-[calc(100%-1rem)] mask-r-to-100%"> + <img onclick="return changeUrl('{{ .ArticleLink }}');" class="h-full min-h-24 w-full cursor-pointer select-none object-cover block absolute" src="{{ printf $.PhotoStorage.Thumbnail320p.BaseUrl .Thumbnail }}"> <div class="w-full h-6 flex flex-row mt-auto relative"> <div class="flex-1/2 flex flex-row justify-center select-none bg-linear-180 {{ if .Liked }}from-accent-deep/40 to-accent-deep/80 text-background-dark{{ else }}from-accent-light/40 to-accent-light/80 text-main-hard{{ end }}"> <svg viewBox="0 0 24 24"><use href="#icon-like"/></svg> @@ -14,7 +14,7 @@ </div> </div> </div> - <div class="grow w-full h-fit flex flex-col justify-center my-auto py-1 px-1"> + <div class="grow w-full h-fit flex flex-col justify-center my-auto py-1 px-2"> <div class="flex flex-row gap-2 max-w-full"> <a href="{{ .ArticleLink }}" onclick="return changeUrl('{{ .ArticleLink }}');" class="grow-2 text-base cursor-pointer font-m-plus"> <span class="font-extrabold">{{ .Title }}</span> diff --git a/views/partials/general-page-header.html b/views/partials/general-page-header.html index 3b8a6c8..a75d32f 100644 --- a/views/partials/general-page-header.html +++ b/views/partials/general-page-header.html @@ -1,8 +1,8 @@ -<title>{{ .Title }} // SAYA.UZ</title> +<title>{{ or .PageTitle .Title }}</title> <div class="flex flex-row mb-2"> {{ block "header" . }}{{ end }} <h1 - class="text-4xl font-m-plus font-bold italic text-shadow-[0_2px_2px_var(--color-main-soft)] text-left my-auto" + class="text-4xl font-m-plus font-bold italic text-shadow-[0_0.125rem_0.125rem_var(--color-main-soft)] text-left my-auto" > {{ .Title }} </h1> diff --git a/views/partials/global-map-widget.html b/views/partials/global-map-widget.html new file mode 100644 index 0000000..f50020f --- /dev/null +++ b/views/partials/global-map-widget.html @@ -0,0 +1,109 @@ +<div + id="map-container" + class="bg-paper bg-background-dark flex z-10 w-full h-full" +></div> + +<script> +(function() { + var map; + var markers = L.markerClusterGroup(); + const THUMB_BASE = "{{ .PhotoStorage.Thumbnail320p.BaseUrl }}"; + + function createCustomIcon(color, borderColor = "var(--color-main-soft)") { + const svgIcon = ` + <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> + <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" + fill="${color}" + stroke="${borderColor}" + stroke-width="1" + stroke-linejoin="round"/> + </svg> + `; + + return L.divIcon({ + html: svgIcon, + className: 'custom-svg-marker', + iconSize: [32, 32], + iconAnchor: [16, 32], + popupAnchor: [0, -32] + }); + } + + function initLocationMap() { + map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], {{ .MapLocationZoom }}); + + protomapsL.leafletLayer({ + url: '{{ .StaticStorage.Map.BaseUrl }}/{{ .StaticStorage.Map.PMTiles }}', + maxZoom: 15, + flavor: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light', + lang: '{{ .Lang }}' + }).addTo(map); + + {{- with .ZoomPosition }} + L.control.zoom({ + position: '{{ . }}' + }).addTo(map); + {{- end }} + + map.addLayer(markers); + } + + function markerToHsl(str, newCoef) { + let hash = 0; + for (let i = 0; i < str.length / 2; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash); + hash %= 360; + } + + return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)]; + } + + function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1, toHighlight = false) { + const [h, s, l] = markerToHsl(title, newCoef); + const color = `hsl(${h} ${s}% ${l}%)`; + + if (relativeErrorMeters != 0) { + var circle = L.circle([x, y], { + color: color, + fillColor: color, + fillOpacity: 0.15, + radius: relativeErrorMeters + }).addTo(map); + } + + var marker = L.marker([x, y], { + icon: createCustomIcon(color), + title: title, + }).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-12 h-12 mr-1" src="${THUMB_BASE.replace('%s', thumbnail)}"> + <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> + </div>`); + + markers.addLayer(marker); + + if (toHighlight) { + markers.zoomToShowLayer(marker, () => marker.openPopup()); + } + } + + initLocationMap(); + {{- range .MapMarkers }} + mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }}, {{ .ToHighlight }}); + {{- end }} + + function onResize() { + setTimeout(() => { + if (map) map.invalidateSize(); + }, 300); + } + window.addEventListener('resize', onResize); + new MutationObserver((muts, obs) => { + if (!document.getElementById('map-container')) { + window.removeEventListener('resize', onResize); + obs.disconnect(); + } + }).observe(document.body, { childList: true, subtree: true }); +})(); +</script> diff --git a/views/partials/personal-page-status.html b/views/partials/personal-page-status.html index 078fb24..41033a9 100644 --- a/views/partials/personal-page-status.html +++ b/views/partials/personal-page-status.html @@ -1,4 +1,4 @@ -<div id="{{ .StatusId }}" class='z-26 w-full inset-shadow-[0_0_0.4rem_black] +<div id="{{ .StatusId }}" class='z-26 w-full inset-shadow-elevation-6 {{if eq .Status "OK"}}bg-green-700 text-green-100 {{else if eq .Status "Failed"}}bg-red-700 text-red-100 {{else}}bg-paper bg-background-dark text-main-soft{{end}} |