diff --git a/web/audio/achievement-unlocked.mp3 b/web/audio/achievement-unlocked.mp3
new file mode 100644
index 0000000..9940318
Binary files /dev/null and b/web/audio/achievement-unlocked.mp3 differ
diff --git a/web/index.html b/web/index.html
index 1ceb0e7..add0d6d 100644
--- a/web/index.html
+++ b/web/index.html
@@ -37,8 +37,14 @@
+
+
+
@@ -54,6 +60,7 @@
+
diff --git a/web/script.js b/web/script.js
index f931f58..6e37fb1 100644
--- a/web/script.js
+++ b/web/script.js
@@ -8,10 +8,17 @@ const catImg = document.getElementById("cat");
const heartImg = document.getElementById("heart");
const volumeSlider = document.getElementById("volume-slider");
const currentVolumeLabel = document.getElementById("current-volume-label");
+const listenerCountLabel = document.getElementById("listener-count");
+const notificationContainer = document.getElementById("notification");
+const notificationTitle = document.getElementById("notification-title");
+const notificationBody = document.getElementById("notification-body");
+
const clickAudio = document.getElementById("click-audio");
const clickReleaseAudio = document.getElementById("click-release-audio");
const meowAudio = document.getElementById("meow-audio");
-const listenerCountLabel = document.getElementById("listener-count");
+const achievementUnlockedAudio = document.getElementById(
+ "achievement-unlocked-audio",
+);
let isPlaying = false;
let isFading = false;
@@ -19,6 +26,7 @@ let currentAudio;
let maxVolume = 100;
let currentVolume = 0;
let saveVolumeTimeout = null;
+let meowCount = 0;
let ws = connectToWebSocket();
function playAudio() {
@@ -134,7 +142,9 @@ function enableSpaceBarControl() {
}
function connectToWebSocket() {
- const ws = new WebSocket(`ws://${location.host}/ws`);
+ const ws = new WebSocket(
+ `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}/ws`,
+ );
ws.onmessage = (event) => {
console.log(event.data);
@@ -189,6 +199,34 @@ function loadInitialVolume() {
document.getElementById("volume-slider-container").style.display = "flex";
}
+function loadMeowCount() {
+ const lastMeowCount = localStorage.getItem("meowCount");
+ if (!lastMeowCount) {
+ meowCount = 0;
+ } else {
+ const n = Number.parseInt(lastMeowCount);
+ if (Number.isNaN(n)) {
+ meowCount = 0;
+ } else {
+ meowCount += n;
+ }
+ }
+}
+
+function showNotification(title, content, duration) {
+ notificationTitle.innerText = title;
+ notificationBody.innerText = content;
+ notificationContainer.style.display = "block";
+ notificationContainer.style.animation = "0.5s linear 0s notification-fade-in";
+ setTimeout(() => {
+ notificationContainer.style.animation =
+ "0.5s linear 0s notification-fade-out";
+ setTimeout(() => {
+ notificationContainer.style.display = "none";
+ }, 450);
+ }, duration);
+}
+
playBtn.onmousedown = () => {
clickAudio.play();
document.addEventListener(
@@ -231,8 +269,19 @@ meowAudio.onplay = () => {
heartImg.style.display = "none";
heartImg.style.animation = "";
}, 900);
+
+ meowCount += 1;
+ localStorage.setItem("meowCount", `${meowCount}`);
+
+ if (meowCount === 100) {
+ showNotification("a little chatty", "make milo meow 100 times", 5000);
+ achievementUnlockedAudio.play();
+ }
};
+// don't wanna jumpscare ppl
+achievementUnlockedAudio.volume = 0.05;
+
window.addEventListener("offline", () => {
ws = null;
});
@@ -241,6 +290,7 @@ window.addEventListener("online", () => {
ws = connectToWebSocket();
});
+loadMeowCount();
loadInitialVolume();
animateCat();
enableSpaceBarControl();
diff --git a/web/style.css b/web/style.css
index c9383c1..6698108 100644
--- a/web/style.css
+++ b/web/style.css
@@ -1,6 +1,7 @@
:root {
font-family: monospace;
--text: #4c4f69;
+ --surface0: #ccd0da;
--surface1: #bcc0cc;
--base: #eff1f5;
--lavender: #7287fd;
@@ -149,17 +150,6 @@ a {
display: flex;
}
-#volume-slider-container {
- display: none;
- justify-content: start;
- align-items: center;
-}
-
-#volume-slider-container > output {
- color: var(--text);
- margin-right: 1rem;
-}
-
@media screen and (-webkit-min-device-pixel-ratio: 0) {
input[type="range"] {
overflow: hidden;
@@ -333,3 +323,55 @@ input[type="range"]::-ms-fill-upper {
right: 50%;
height: 15px;
}
+
+#volume-slider-container {
+ display: none;
+ justify-content: start;
+ align-items: center;
+}
+
+#volume-slider-container > output {
+ color: var(--text);
+ margin-right: 1rem;
+}
+
+aside#notification {
+ display: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ background: var(--surface0);
+ padding: 1rem 2rem;
+ margin: 2rem;
+ border: 2px solid var(--text);
+}
+
+aside#notification > p {
+ margin: 0;
+}
+
+aside#notification > #notification-title {
+ font-weight: 700;
+ margin-bottom: 0.5rem;
+}
+
+@keyframes notification-fade-in {
+ 0% {
+ display: block;
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes notification-fade-out {
+ 0% {
+ opactiy: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}