diff --git a/.eleventy.js b/.eleventy.js index fd83efc..f49e556 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -27,45 +27,47 @@ module.exports = function(eleventyConfig) { })); }); -eleventyConfig.addPassthroughCopy("static"); + eleventyConfig.addPassthroughCopy("static"); -eleventyConfig.addNunjucksFilter("alternateLanguages", function(collection, postId, currentLanguageKey) { - return collection.filter(post => - post.data.postId === postId && post.data.langKey !== currentLanguageKey - ) - .map(post => ({ - lang: post.data.langKey, - url: post.url, - title: post.data.title - })) -}); - -eleventyConfig.addFilter("absoluteUrl", function(path) { - const base = "https://adrianvic.github.io"; - return base + path; -}); - -eleventyConfig.addFilter("postDate", (dateObj) => { - return dateObj.toLocaleString(undefined, { - year: "numeric", - month: "numeric", - day: "numeric", - timeZone: "America/Sao_Paulo" + eleventyConfig.addNunjucksFilter("alternateLanguages", function(collection, postId, currentLanguageKey) { + return collection.filter(post => + post.data.postId === postId && post.data.langKey !== currentLanguageKey + ) + .map(post => ({ + lang: post.data.langKey, + url: post.url, + title: post.data.title + })) }); -}); -eleventyConfig.addNunjucksFilter("smartTitle", function(str) { - if (!str) return ""; - const smallWords = ["a","an","and","at","but","by","for","in","nor","of","on","or","so","the","to","up","yet", - "e","de","do","da","dos","das","a","o","um","uma","em","por","para","com","no","na","nos"]; + eleventyConfig.addFilter("absoluteUrl", function(path) { + const base = "https://adrianvic.github.io"; + return base + path; + }); + + eleventyConfig.addFilter("postDate", (dateObj) => { + if (!dateObj) return ""; + return dateObj.toLocaleString(undefined, { + year: "numeric", + month: "numeric", + day: "numeric", + timeZone: "America/Sao_Paulo" + }); + }); + + eleventyConfig.addNunjucksFilter("smartTitle", function(str) { + if (!str) return ""; + const smallWords = ["a","an","and","at","but","by","for","in","nor","of","on","or","so","the","to","up","yet", + "e","de","do","da","dos","das","a","o","um","uma","em","por","para","com","no","na","nos"]; return str.toLowerCase().split(" ").map((word, i) => { if (i === 0) return word.charAt(0).toUpperCase() + word.slice(1); return smallWords.includes(word) ? word : word.charAt(0).toUpperCase() + word.slice(1); }).join(" "); }); + return { dir: { output: "docs" } }; -} \ No newline at end of file +}; diff --git a/_data/i18n.js b/_data/i18n.js index 0776e36..ab17402 100644 --- a/_data/i18n.js +++ b/_data/i18n.js @@ -74,7 +74,11 @@ module.exports = { websiteDescription: "Personal website/blog of Adrian Victor.", miscellaneous: "Miscellaneous", i88x31hover: "Click to expand", - lastEditedIn: "last edited in" + lastEditedIn: "last edited in", + permissionIssue: "Permission issue", + permissionIssueNotificationContent: "Unable to continue playing background music, please enable audio autoplay for this website.", + notificationDefaultHint: "Click to dismiss" + }, pt: { language: "português", @@ -125,6 +129,9 @@ module.exports = { websiteDescription: "Website/blog pessoal de Adrian Victor.", miscellaneous: "Miscelâneo", i88x31hover: "Clique para expandir", - lastEditedIn: "editado por último em" + lastEditedIn: "editado por último em", + permissionIssue: "Problema de permissão", + permissionIssueNotificationContent: "Não foi possivel continuar tocando a música de fundo, por favor habilite reprodução automática de áudio para esse website.", + notificationDefaultHint: "Clique para ignorar" } }; \ No newline at end of file diff --git a/_includes/header.njk b/_includes/header.njk index 466f7c3..08968f9 100644 --- a/_includes/header.njk +++ b/_includes/header.njk @@ -8,7 +8,10 @@ by: "{{ i18n[langKey].by | safe }}", options: "{{ i18n[langKey].options | safe }}", hideBackground: "{{ i18n[langKey].hideBackground | safe }}", - back: "{{ i18n[langKey].back | safe }}" + back: "{{ i18n[langKey].back | safe }}", + permissionIssue: "{{ i18n[langKey].permissionIssue | safe }}", + permissionIssueNotificationContent: "{{ i18n[langKey].permissionIssueNotificationContent | safe }}", + notificationDefaultHint: "{{ i18n[langKey].notificationDefaultHint | safe }}", } diff --git a/_includes/post.njk b/_includes/post.njk index e0c19c3..3fb1fbd 100644 --- a/_includes/post.njk +++ b/_includes/post.njk @@ -9,7 +9,7 @@ title: Adrian Victor:Blog

{{ postTitle }}

-

{{ authors or "Adrian Victor" }} - {{ date | postDate }}

+

{{ authors or "Adrian Victor" }} - {{ date | postDate }}{% if lastModified | postDate !== date | postDate %} ({{ i18n[langKey].lastEditedIn }} {{ lastModified | postDate }}){% endif %}

{% if altLanguages.length > 0 %} {{ i18n[langKey].availableInOtherLanguages }}: {% for alt in altLanguages %} diff --git a/static/main.css b/static/main.css index 09f0cb9..5db40e4 100644 --- a/static/main.css +++ b/static/main.css @@ -184,8 +184,7 @@ aside.metromenu { width: 30vw; background-color: black; height: 100vh; - transition: 0.2s; - transition-timing-function: cubic-bezier(0.1, 0.2, 0.3, 0.955); + transition: transform 0.6s cubic-bezier(0.19, 1, 0.22, 1); padding: 2em; } @@ -213,6 +212,28 @@ aside.metromenu #content { /* Global classes and IDs */ +.notificationBox { + transition: transform 1s cubic-bezier(0.19, 1, 0.22, 1); + width: fit-content; + position: fixed; + bottom: 2em; + left: 0; + background-color: black; + transform: translateX(-100%); + padding: 1em; + outline: thin solid var(--theme-color); + margin-right: 8em; + max-width: 40em; +} + +.notificationBox.shown { + transform: none; +} + +.notificationBox h1 { + font-size: x-large; +} + li.inlineList { display: inline; } diff --git a/static/scripts/music.js b/static/scripts/music.js index c11da94..0aedc43 100644 --- a/static/scripts/music.js +++ b/static/scripts/music.js @@ -1,4 +1,5 @@ // This script handles the playback of music in the header's miniplayer ;) +import { showNotification } from './notification.js'; const body = document.querySelector("body"); const musicdiv = document.getElementById("music"); @@ -42,7 +43,7 @@ optionsAside.classList.add("metromenu");

Volume

- +

${headeri18n.hideBackground}

@@ -54,6 +55,9 @@ optionsAside.classList.add("metromenu"); } body.appendChild(optionsAside); +document.getElementById("volume").addEventListener("input", (e) => { + setVolume(e.target.value / 100); +}); // dirty workaround to replace inline function calling in the volume input const toggleIMG = document.querySelector('#sound'); toggleIMG.addEventListener('click', () => { @@ -146,11 +150,21 @@ let audio = new Audio(`/static/music/${audioSelect.value}`); const savedTime = localStorage.getItem("audioTime"); const savedVolume = localStorage.getItem("volume"); + +if (savedVolume !== null) { + audio.volume = parseFloat(savedVolume); +} else { + audio.volume = 0.8; +} + const wasPlaying = localStorage.getItem("audioPlaying") === 'true'; function play() { - audio.volume = localStorage.getItem("volume"); - audio.play(); + audio.volume = localStorage.getItem("volume") ?? 0.8; + audio.play().catch(() => { + stop(); + showNotification(headeri18n.permissionIssue, headeri18n.permissionIssueNotificationContent, 5000); + });; localStorage.setItem("audioPlaying", "true") toggleIMG.src = "/static/images/sound-on.png" console.log(`[Music Player] playing ${audioSelect.value}`) diff --git a/static/scripts/notification.js b/static/scripts/notification.js new file mode 100644 index 0000000..a05b793 --- /dev/null +++ b/static/scripts/notification.js @@ -0,0 +1,45 @@ +import { registerElementHint } from "./tips.js"; + +const notificationBox = document.createElement('div'); +notificationBox.classList.add('notificationBox'); + +export async function showNotification(title, subtitle, time, hint) { + if (!hint) { + hint = headeri18n.notificationDefaultHint; + } + const notificationBox = document.createElement('div'); + notificationBox.classList.add('notificationBox'); + notificationBox.dataset.tip = hint; + + const notificationTitle = document.createElement('h1'); + notificationTitle.innerHTML = title; + + const notificationSubtitle = document.createElement('p'); + notificationSubtitle.innerHTML = subtitle; + + notificationBox.appendChild(notificationTitle); + notificationBox.appendChild(notificationSubtitle); + document.querySelector('body').appendChild(notificationBox); + + registerElementHint(notificationBox); + + let clicked = false; + + notificationBox.addEventListener('click', () => { + hideNotification(notificationBox); + }) + + requestAnimationFrame(() => { + notificationBox.classList.add('shown'); + }); + await new Promise(r => setTimeout(r, time)); + if (!clicked) { + hideNotification(notificationBox); + } +} + +async function hideNotification(notificationBox) { + notificationBox.classList.remove('shown'); + await new Promise(r => setTimeout(r, 1000)); + notificationBox.remove(); +} \ No newline at end of file diff --git a/static/scripts/tips.js b/static/scripts/tips.js index 8100cd6..acc69be 100644 --- a/static/scripts/tips.js +++ b/static/scripts/tips.js @@ -1,8 +1,4 @@ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); -if (isMobile) { - return; -} - const body = document.querySelector('body'); const elements = document.querySelectorAll('[data-tip]'); const hint = document.querySelector("#headerSubtitle"); @@ -10,36 +6,11 @@ const hintPanelDefaultText = hint.innerHTML; let fixedHint; let currentObserver; -elements.forEach(el => { - el.addEventListener('mouseenter', function() { - cleanup(); - - if (currentObserver) { - currentObserver.disconnect(); - } - - currentObserver = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (!entry.isIntersecting) { - fixedHint = document.createElement('p'); - fixedHint.id = "fixedHint"; - fixedHint.innerHTML = el.dataset.tip; - fixedHint.setAttribute('aria-hidden', 'true'); - body.appendChild(fixedHint); - } else { - hint.innerHTML = el.dataset.tip; - } - }) - }); - - hint.innerHTML = el.dataset.tip; - currentObserver.observe(hint); - }); - - el.addEventListener('mouseleave', function() { - cleanup(); - }); -}) +if (!isMobile) { + elements.forEach(el => { + registerElementHint(el); + }) +} function cleanup() { hint.innerHTML = hintPanelDefaultText; @@ -52,3 +23,34 @@ function cleanup() { currentObserver = null; } } + +export function registerElementHint(el) { + el.addEventListener('mouseenter', function() { + cleanup(); + + if (currentObserver) { + currentObserver.disconnect(); + } + + currentObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (!entry.isIntersecting) { + fixedHint = document.createElement('p'); + fixedHint.id = "fixedHint"; + fixedHint.innerHTML = el.dataset.tip; + fixedHint.setAttribute('aria-hidden', 'true'); + body.appendChild(fixedHint); + } else { + hint.innerHTML = el.dataset.tip; + } + }) + }); + + hint.innerHTML = el.dataset.tip; + currentObserver.observe(hint); + }); + + el.addEventListener('mouseleave', function() { + cleanup(); + }); +} \ No newline at end of file