mirror of
https://github.com/NotXia/notxia.github.io.git
synced 2025-12-14 19:01:51 +01:00
Add easter egg achievement
This commit is contained in:
13
src/App.vue
13
src/App.vue
@ -11,16 +11,25 @@
|
||||
</div>
|
||||
|
||||
<Cookie />
|
||||
<EastereggBanner ref="easteregg" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import { applyTheme } from "./utilities/theme_handler";
|
||||
import Cookie from "@/components/cookie/Cookie.vue";
|
||||
|
||||
import EastereggBanner from "@/components/easteregg-banner/EastereggBanner.vue";
|
||||
|
||||
const easteregg = ref();
|
||||
|
||||
onMounted(() => {
|
||||
applyTheme();
|
||||
|
||||
document.addEventListener("easteregg", (e) => {
|
||||
// @ts-ignore
|
||||
easteregg.value.show(e.detail);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
BIN
src/assets/images/future.png
Normal file
BIN
src/assets/images/future.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@ -5,16 +5,14 @@
|
||||
<canvas ref="canvas_cookie" :width="canvas_width" :height="canvas_height"></canvas>
|
||||
</div>
|
||||
|
||||
<div ref="banner" class="fixed bottom-0 left-0 z-50">
|
||||
<div class="border bg-slate-200/90 border-slate-700 dark:bg-slate-800/90 dark:border-slate-400 rounded w-fit lg:w-1/2 p-5 m-5">
|
||||
<p class="text-sm">{{ t("cookie policy title") }}</p>
|
||||
<div class="text-xs">
|
||||
<span>{{ t("cookie policy") }} <button :onclick="throwCookie" class="underline">{{ t("cookie policy link") }}</button></span>
|
||||
</div>
|
||||
<div class="flex justify-end mt-2 text-sm">
|
||||
<button :onclick="refuseCookieBanner" class="mx-1 hover:text-slate-500 dark:hover:text-slate-300">{{ t("reject") }}</button>
|
||||
<button :onclick="acceptCookieBanner" class="rounded p-2 mx-1 bg-slate-300 hover:bg-slate-400 dark:bg-slate-700 dark:hover:bg-slate-600">{{ t("accept") }}</button>
|
||||
</div>
|
||||
<div ref="banner" class="fixed bottom-0 left-0 z-50 border bg-slate-200/90 border-slate-700 dark:bg-slate-800/90 dark:border-slate-400 rounded w-fit lg:w-1/2 p-5 m-5">
|
||||
<p class="text-sm">{{ t("cookie policy title") }}</p>
|
||||
<div class="text-xs">
|
||||
<span>{{ t("cookie policy") }} <button :onclick="throwCookie" class="underline">{{ t("cookie policy link") }}</button></span>
|
||||
</div>
|
||||
<div class="flex justify-end mt-2 text-sm">
|
||||
<button :onclick="refuseCookieBanner" class="mx-1 hover:text-slate-500 dark:hover:text-slate-300">{{ t("reject") }}</button>
|
||||
<button :onclick="acceptCookieBanner" class="rounded p-2 mx-1 bg-slate-300 hover:bg-slate-400 dark:bg-slate-700 dark:hover:bg-slate-600">{{ t("accept") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -29,6 +27,7 @@
|
||||
import { randomInt, random } from "@/utilities/random";
|
||||
import { shouldShowCookie, acceptCookie, refuseCookie } from "@/utilities/cookie_handler";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
|
||||
|
||||
const { t } = useI18n({ messages: {
|
||||
"en": {
|
||||
@ -118,6 +117,7 @@
|
||||
}
|
||||
|
||||
function throwCookie() {
|
||||
addFoundEasterEgg("cookie");
|
||||
if (!engine) { return; }
|
||||
|
||||
// Creates a cookie at the bottom of visible screen
|
||||
|
||||
70
src/components/easteregg-banner/EastereggBanner.vue
Normal file
70
src/components/easteregg-banner/EastereggBanner.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="fixed top-0 left-0 w-full pointer-events-none">
|
||||
|
||||
<div :class="`border rounded-sm p-3 px-5 my-2 mx-auto w-fit max-w-xs md:max-w-md pointer-events-auto
|
||||
bg-slate-200/90 border-slate-700 dark:bg-slate-800/90 dark:border-slate-400
|
||||
transition-opacity ${show_banner ? 'opacity-100 duration-300' : 'opacity-0 duration-200'}`"
|
||||
@click="dismiss">
|
||||
|
||||
<div class="flex text-sm">
|
||||
<div class="flex-1">
|
||||
<CookieEgg v-if="easteregg === 'cookie'" />
|
||||
<FutureEgg v-if="easteregg === 'future'" />
|
||||
|
||||
<div class="mt-1 text-center">
|
||||
<p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ t("easter eggs found") }}</p>
|
||||
<p v-if="found_eastereggs === total_eastereggs">{{ t("all easter eggs found") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { getFoundEasterEggsCount, getTotalEasterEggsCount } from "@/utilities/easteregg_handler";
|
||||
import CookieEgg from "./eggs/Cookie.vue";
|
||||
import FutureEgg from "./eggs/Future.vue";
|
||||
|
||||
const show_banner = ref(false);
|
||||
const easteregg = ref("");
|
||||
|
||||
const total_eastereggs = ref(getTotalEasterEggsCount());
|
||||
const found_eastereggs = ref(getFoundEasterEggsCount());
|
||||
|
||||
const { t } = useI18n({ messages: {
|
||||
"en": {
|
||||
"easter eggs found": "easter eggs found",
|
||||
"all easter eggs found": "You found all the easter eggs 🥚"
|
||||
},
|
||||
"it": {
|
||||
"easter eggs found": "easter egg trovati",
|
||||
"all easter eggs found": "Hai trovato tutti gli easter egg 🥚"
|
||||
}
|
||||
} });
|
||||
|
||||
|
||||
function show(easteregg_name:string) {
|
||||
easteregg.value = easteregg_name;
|
||||
found_eastereggs.value = getFoundEasterEggsCount();
|
||||
console.log(getFoundEasterEggsCount())
|
||||
show_banner.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
dismiss();
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
show_banner.value = false;
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
show
|
||||
})
|
||||
</script>
|
||||
32
src/components/easteregg-banner/eggs/Cookie.vue
Normal file
32
src/components/easteregg-banner/eggs/Cookie.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
|
||||
<div class="flex justify-center text-sm">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
|
||||
<img :src="image" alt="" class="h-full w-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 ml-2">
|
||||
<p class="font-bold text-base">{{ t("title") }}</p>
|
||||
<p>{{ t("description") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import image from "@/assets/images/cookie.png";
|
||||
|
||||
const { t } = useI18n({ messages: {
|
||||
"en": {
|
||||
"title": "Who wants a cookie?",
|
||||
"description": "It appears that you read the cookie policy"
|
||||
},
|
||||
"it": {
|
||||
"title": "Chi vuole un biscotto?",
|
||||
"description": "Sembra che hai letto l'informativa cookie"
|
||||
}
|
||||
} });
|
||||
</script>
|
||||
32
src/components/easteregg-banner/eggs/Future.vue
Normal file
32
src/components/easteregg-banner/eggs/Future.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
|
||||
<div class="flex text-sm">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="w-10 h-10 flex items-center justify-center overflow-hidden">
|
||||
<img :src="image" alt="" class="h-full w-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 ml-2">
|
||||
<p class="font-bold text-base">{{ t("title") }}</p>
|
||||
<p>{{ t("description") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import image from "@/assets/images/future.png";
|
||||
|
||||
const { t } = useI18n({ messages: {
|
||||
"en": {
|
||||
"title": "Wonderful future",
|
||||
"description": "What will happen?"
|
||||
},
|
||||
"it": {
|
||||
"title": "Che bello il futuro",
|
||||
"description": "Cosa ci riserverà?"
|
||||
}
|
||||
} });
|
||||
</script>
|
||||
28
src/utilities/easteregg_handler.ts
Normal file
28
src/utilities/easteregg_handler.ts
Normal file
@ -0,0 +1,28 @@
|
||||
const EASTER_EGGS = ["cookie", "future"];
|
||||
|
||||
export function addFoundEasterEgg(name:string):void {
|
||||
if (!EASTER_EGGS.includes(name)) { return; }
|
||||
|
||||
let found_eastereggs = new Set( JSON.parse(localStorage.getItem("eastereggs") ?? "[]") );
|
||||
const is_new = !found_eastereggs.has(name);
|
||||
|
||||
found_eastereggs.add(name);
|
||||
localStorage.setItem("eastereggs", JSON.stringify([...found_eastereggs]));
|
||||
|
||||
if (is_new) {
|
||||
// To trigger the banner
|
||||
document.dispatchEvent(new CustomEvent("easteregg", { detail: name }));
|
||||
}
|
||||
}
|
||||
|
||||
export function getFoundEasterEggs():string[] {
|
||||
return JSON.parse(localStorage.getItem("eastereggs") ?? "[]");
|
||||
}
|
||||
|
||||
export function getFoundEasterEggsCount():number {
|
||||
return getFoundEasterEggs().length;
|
||||
}
|
||||
|
||||
export function getTotalEasterEggsCount():number {
|
||||
return EASTER_EGGS.length;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex h-full justify-center relative">
|
||||
<div class="absolute top-0 left-0 w-full">
|
||||
<div data-tooltip-target="tooltip-future" class="relative w-6 h-2 mx-auto z-50">
|
||||
<div data-tooltip-target="tooltip-future" class="relative w-6 h-2 mx-auto z-50" @mouseover="() => addFoundEasterEgg('future')">
|
||||
</div>
|
||||
<div id="tooltip-future" role="tooltip"
|
||||
class="absolute z-10 invisible inline-block px-2 py-1 text-xs font-medium transition-opacity duration-1000 rounded-lg opacity-0 tooltip">
|
||||
@ -52,6 +52,7 @@
|
||||
import locale from "./locale.js";
|
||||
import { initTooltips } from "flowbite";
|
||||
import { onMounted } from "vue";
|
||||
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
|
||||
|
||||
const timeline_locale = locale;
|
||||
// @ts-ignore
|
||||
|
||||
Reference in New Issue
Block a user