Files
pihkaal-me/app/components/Contact/BottomScreen/Buttons.vue
Pihkaal 692876763c
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m33s
feat(cv): add
2026-02-27 14:39:10 +01:00

168 lines
4.0 KiB
Vue

<script setup lang="ts">
import ButtonSelector from "~/components/Common/ButtonSelector.vue";
import { promiseTimeout } from "@vueuse/core";
const { onRender } = useScreen();
const store = useContactStore();
const achievements = useAchievementsStore();
const confirmationModal = useConfirmationModal();
const { assets } = useAssets((a) => a.images.contact.bottomScreen.buttons);
const BUTTONS = {
git: {
position: [31, 32],
action: "open",
url: "https://git.pihkaal.me",
text: "git.pihkaal.me",
},
email: {
position: [31, 64],
action: "copy",
url: "hello@pihkaal.me",
text: "hello@pihkaal.me",
},
linkedin: {
position: [31, 96],
action: "open",
url: "https://linkedin.com/in/stevancorre/",
text: "/in/stevancorre/",
},
cv: {
position: [31, 128],
action: "open",
url: "https://pihkaal.me/cv.pdf",
text: "/cv.pdf",
},
} as const satisfies Record<
string,
{
position: [number, number];
action: "copy" | "open";
url: string;
text: string;
}
>;
const { selected, pressed, selectorPosition } = useButtonNavigation({
buttons: {
git: [26, 27, 202, 42],
email: [26, 59, 202, 42],
linkedin: [26, 91, 202, 42],
cv: [26, 123, 202, 42],
},
navigation: {
git: {
down: "email",
},
email: {
up: "git",
down: "linkedin",
},
linkedin: {
up: "email",
down: "cv",
},
cv: {
up: "linkedin",
},
},
initialButton: "git",
onActivate: (button) => handleActivateA(button),
disabled: computed(() => store.isIntro || store.isOutro),
selectorAnimation: {
duration: 0.075,
ease: "none",
},
});
const handleActivateA = async (button: typeof selected.value) => {
const { action, url } = BUTTONS[button];
const verb = $t(`contact.actions.${button}`);
if (action === "copy") {
try {
await navigator.clipboard.writeText(url);
store.pushNotification($t("contact.copiedToClipboard", { item: verb }));
} catch (error) {
console.error("Failed to copy to clipboard:", error);
}
} else {
confirmationModal.open({
text: $t("contact.openUrl", { url: url.replace(/^https?:\/\//, "") }),
aLabel: $t("common.yes"),
bLabel: $t("common.no"),
onActivateA: async () => {
store.pushNotification($t("contact.opened", { item: verb }));
await promiseTimeout(100);
await navigateTo(url, { open: { target: "_blank " } });
await promiseTimeout(500);
if (button === "git") {
achievements.unlock("contact_git_visit");
}
},
});
}
};
const handleActivateB = () => {
if (store.isIntro || store.isOutro) return;
store.animateOutro();
};
onRender((ctx) => {
ctx.globalAlpha = store.isIntro
? store.intro.stage3Opacity
: store.outro.stage1Opacity;
ctx.font = "10px NDS10";
ctx.fillStyle = "#515151";
for (const [
buttonName,
{
text,
position: [x, y],
},
] of Object.entries(BUTTONS)) {
const button = buttonName as typeof selected.value;
const isPressed = pressed.value === button;
assets[isPressed ? "buttonPressed" : "button"].draw(ctx, x, y);
const pressedKey: `${typeof selected.value}Pressed` = `${button}Pressed`;
assets[isPressed ? pressedKey : button].draw(ctx, x + 1, y + 1);
ctx.fillText(text, x + 36, y + 11 + 9);
}
});
</script>
<template>
<ButtonSelector
:rect="selectorPosition"
:opacity="
store.isIntro ? store.intro.stage3Opacity : store.outro.stage1Opacity
"
/>
<CommonConfirmationModal />
<CommonButtons
:y-offset="
store.isIntro ? store.intro.barOffsetY : confirmationModal.buttonsYOffset
"
:opacity="
store.isIntro
? store.intro.stage3Opacity
: store.isOutro
? store.outro.stage2Opacity
: 1
"
:b-label="$t('common.quit')"
:a-label="$t(`contact.actions.${BUTTONS[selected].action}`)"
no-keyboard-a
@activate-a="handleActivateA(selected)"
@activate-b="handleActivateB"
/>
</template>