Add about me section

This commit is contained in:
2023-03-08 19:37:26 +01:00
parent 96312bac80
commit 63beeeb823
14 changed files with 304 additions and 24 deletions

BIN
src/assets/images/moon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

BIN
src/assets/images/sun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -48,14 +48,6 @@
border-radius: 0.2rem;
border: 1px solid #424242;
}
.gr_grid_book_container:first-child {
margin-left: 0;
margin-right: 0.5rem;
}
.gr_grid_book_container:last-child {
margin-left: 0.5rem;
margin-right: 0;
}
.gr_grid_book_container > * img {
height: 100%;

View File

@ -11,6 +11,8 @@
<CookieEgg v-if="easteregg === 'cookie'" />
<FutureEgg v-if="easteregg === 'future'" />
<SomethingEgg v-if="easteregg === 'change-something'" />
<PictureBrightEgg v-if="easteregg === 'picture-bright'" />
<PictureNoLightEgg v-if="easteregg === 'picture-nolights'" />
<div class="mt-1 text-center">
<p v-if="found_eastereggs != total_eastereggs">{{ found_eastereggs }}/{{ total_eastereggs }} {{ t("easter eggs found") }}</p>
@ -31,6 +33,8 @@
import CookieEgg from "./eggs/Cookie.vue";
import FutureEgg from "./eggs/Future.vue";
import SomethingEgg from "./eggs/Something.vue";
import PictureBrightEgg from "./eggs/PictureBright.vue";
import PictureNoLightEgg from "./eggs/PictureNoLight.vue";
const show_banner = ref(false);
const easteregg = ref("");
@ -41,7 +45,7 @@
const { t } = useI18n({ messages: {
"en": {
"easter eggs found": "easter eggs found",
"all easter eggs found": "You found all the easter eggs 🥚"
"all easter eggs found": "You found all easter eggs 🥚"
},
"it": {
"easter eggs found": "easter egg trovati",

View File

@ -0,0 +1,40 @@
<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/sun.png";
import { getFoundEasterEggs } from "@/utilities/easteregg_handler";
let locale = {
"en": {
"title": "It's bright",
"description": "I don't like the light"
},
"it": {
"title": "È luminoso",
"description": "Non mi piace la luce"
}
};
if (getFoundEasterEggs().includes("picture-nolights")) {
locale["en"]["description"] = "I don't like the light either"
locale["it"]["description"] = "Non mi piace nemmeno la luce"
}
const { t } = useI18n({ messages: locale });
</script>

View File

@ -0,0 +1,40 @@
<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/moon.png";
import { getFoundEasterEggs } from "@/utilities/easteregg_handler";
let locale = {
"en": {
"title": "It's dark",
"description": "I don't like the dark"
},
"it": {
"title": "È buio",
"description": "Non mi piace il buio"
}
};
if (getFoundEasterEggs().includes("picture-bright")) {
locale["en"]["description"] = "I don't like the dark either"
locale["it"]["description"] = "Non mi piace nemmeno il buio"
}
const { t } = useI18n({ messages: locale });
</script>

View File

@ -0,0 +1,108 @@
<template>
<div class="relative">
<div class="flex items-center h-60 w-60">
<img v-show="picture === 'dark'" :src="picture_dark" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'light'" :src="picture_light" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'bright'" :src="picture_bright" alt="" class="max-h-full max-w-full">
<img v-show="picture === 'no light'" :src="picture_nolight" alt="" class="max-h-full max-w-full">
</div>
<div v-if="message" class="absolute bottom-0 left-0 w-full">
<p class="w-fit mx-auto px-2 pt-1 mb-1 bg-gray-200 dark:bg-gray-700">{{ message }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { ref, onMounted, watch } from "vue";
import { addFoundEasterEgg } from "@/utilities/easteregg_handler";
import { useRoute } from "vue-router";
import picture_dark from "@/assets/images/profile/picture-dark.png";
import picture_light from "@/assets/images/profile/picture-light.png";
import picture_bright from "@/assets/images/profile/picture-bright.png";
import picture_nolight from "@/assets/images/profile/picture-nolight.png";
const { t, locale } = useI18n({ messages: {
"en": {
"that's bright": "That's bright",
"better": "Better",
"where lights": "Who turned off the lights?",
"here lights": "Here they are"
},
"it": {
"that's bright": "È luminoso",
"better": "Meglio",
"where lights": "Chi ha spento le luci?",
"here lights": "Eccole"
}
} });
const picture = ref(picture_dark);
const message = ref("picture_dark");
const route = useRoute();
// Finite-state automata to handle theme changes
interface State {
image: string; // Image to display
message: string; // Message to display (string to parse with i18n)
expect: string; // Expected active theme when this state is applied
next: string; // Next state
}
let current_state = document.querySelector("html")?.classList.contains("dark") ? "dark1" : "light1";
let states:Record<string, State> = {
"dark1": { image: "dark", message: "", expect: "dark", next: "bright" },
"bright": { image: "bright", message: "that's bright", expect: "light", next: "dark2" },
"dark2": { image: "dark", message: "better", expect: "dark", next: "light_final" },
"light1": { image: "light", message: "", expect: "light", next: "nolights" },
"nolights": { image: "no light", message: "where lights", expect: "dark", next: "light2" },
"light2": { image: "light", message: "here lights", expect: "light", next: "dark_final" },
"dark_final": { image: "dark", message: "", expect: "dark", next: "light_final" },
"light_final": { image: "light", message: "", expect: "light", next: "dark_final" },
}
function applyState() {
picture.value = states[current_state]?.image;
message.value = t(states[current_state]?.message);
}
function nextState() {
current_state = states[current_state]?.next;
}
onMounted(() => {
applyState();
let observer = new MutationObserver(function(mutations) {
if (route.path !== "/about") { observer.disconnect(); return; } // Disconnect observer if on other routes
const is_dark_theme = document.querySelector("html")?.classList.contains("dark");
if ( states[states[current_state].next].expect === (is_dark_theme ? "dark" : "light") ) {
nextState();
applyState();
if (current_state === "bright") { addFoundEasterEgg("picture-bright"); }
else if (current_state === "nolights") { addFoundEasterEgg("picture-nolights"); }
}
});
// Observes for theme changes
observer.observe((document.querySelector("html") as Node), { attributes: true, attributeFilter: ['class'] });
});
watch(locale, () => { // To update the language of the displayed message
applyState();
});
</script>

View File

@ -1,4 +1,4 @@
const EASTER_EGGS = ["cookie", "future", "change-something"];
const EASTER_EGGS = ["cookie", "future", "change-something", "picture-bright", "picture-nolights"];
export function addFoundEasterEgg(name:string):void {
if (!EASTER_EGGS.includes(name)) { return; }

View File

@ -1,20 +1,124 @@
<template>
<Navbar />
<ScreenCenter>
<main>
<div class="container mx-auto">
<h1 class="text-5xl font-bold">
{{ t("hello") }}
</h1>
<div class="flex flex-col md:flex-row md:justify-between items-center h-full">
<div class="text-xl order-2 md:order-1 [&>*>p]:mb-2">
<h1 class="text-5xl font-bold mb-5">{{ t("about me") }}</h1>
<div v-show="current_locale === 'it'">
<p>
Mi chiamo
<span data-popover-target="popover-name" class="underline decoration-dashed decoration-slate-900/70 dark:decoration-slate-50/70">Xia Tian Cheng</span>,
spesso conosciuto come Xia (cognome) o Riccardo.
Studio informatica all'Università di Bologna e al momento i miei interessi sono orientati verso l'analisi dati e l'intelligenza artificiale.
</p>
<p>
Oltre a premere tasti sulla tastiera,
mi piace leggere,
sperimentare in cucina <span class="text-xs">(preferibilmente con un estintore nei paraggi)</span>
e giocare a giochi strategici.
</p>
<p>
Secondo <a class="hover:underline" href="https://www.16personalities.com">16Personalities</a> sono un
<a class="font-mono hover:underline" href="https://www.16personalities.com/profiles/6b57f54bf1242">Architetto (INTJ)</a>.
Devo dire che la descrizione è decisamente accurata.
</p>
</div>
<div v-show="current_locale === 'en'">
<p>
My name is
<span data-popover-target="popover-name" class="underline decoration-dashed decoration-slate-900/70 dark:decoration-slate-50/70">Xia Tian Cheng</span>,
frequently known as Xia (surname) or Richard.
I am a computer science student at the University of Bologna and currently my interests are focused on data analysis and artificial intelligence.
</p>
<p>
Aside from pressing keys on a keyboard,
I like reading,
cooking <span class="text-xs">(preferably with a fire extinguisher nearby)</span>
and playing strategic games.
</p>
<p>
According to <a class="hover:underline" href="https://www.16personalities.com">16Personalities</a> I'm an
<a class="font-mono hover:underline" href="https://www.16personalities.com/profiles/6b57f54bf1242">Architect (INTJ)</a>.
I must admit that the description is quiet accurate.
</p>
</div>
</div>
<div class="order-1 mb-3 ml-0 md:order-2 md:mb-0 md:ml-10">
<ProfilePicture />
</div>
</div>
<div class="mt-5">
<p class="text-center text-xl">{{ t("reading") }}</p>
<div class="flex justify-center">
<Goodreads class="h-48"/>
</div>
</div>
</main>
</ScreenCenter>
<div data-popover id="popover-name" role="tooltip" class="absolute z-10 invisible inline-block transition-opacity duration-500 opacity-0">
<div class="text-lg px-3 py-2 bg-slate-200 dark:bg-slate-800 rounded">
夏天成
</div>
</div>
</template>
<script setup lang="ts">
import Navbar from "@/components/navbar/Navbar.vue";
import Goodreads from "@/components/goodreads/Goodreads.vue";
import ScreenCenter from "@/components/screen-center/ScreenCenter.vue";
import ProfilePicture from "@/components/profile-picture/ProfilePicture.vue";
import { useI18n } from "vue-i18n";
import locale from "./locale.json";
import { type ComputedRef, computed, onMounted, watch, ref } from "vue";
import { initPopovers } from "flowbite";
const { t } = useI18n({ messages: locale });
const { t, locale } = useI18n({ messages: {
"en": {
"about me": "About me",
"reading": "Currently I'm reading",
"that's bright": "That's bright",
"thanks": "Thanks"
},
"it": {
"about me": "Su di me",
"reading": "Attualmente sto leggendo",
"that's bright": "È luminoso",
"thanks": "Grazie"
}
} });
const current_locale = ref(locale.value);
const my_age: ComputedRef<number> = computed((): number => {
const today = new Date();
const birthDate = new Date(2001, 8, 29);
const month_diff = today.getMonth() - birthDate.getMonth();
let age = today.getFullYear() - birthDate.getFullYear();
if (month_diff < 0 ||
(month_diff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
});
onMounted(() => {
initPopovers();
});
watch(locale, () => {
current_locale.value = locale.value;
});
</script>

View File

@ -1,8 +0,0 @@
{
"en": {
"hello": "Hello"
},
"it": {
"hello": "Ciao"
}
}