import type { DataCollectionItemBase, ProjectsCollectionItem, } from "@nuxt/content"; import gsap from "gsap"; type ProjectItem = Omit< ProjectsCollectionItem, keyof DataCollectionItemBase > & { id: string; }; export type Project = Omit & { description: string; summary: string; tasks: string[]; }; export const useProjectsStore = defineStore("projects", { state: () => ({ projects: [] as Project[], currentProject: 0, loading: true, offsetX: 0, intro: { fadeOpacity: 1, }, outro: { fadeOpacity: 0, }, isIntro: true, isOutro: false, showConfirmationPopup: false, }), actions: { async loadProjects() { const { locale } = useI18n(); this.loading = true; const items = await queryCollection("projects") .order("order", "ASC") .all(); if (!items) throw "Cannot load projects"; this.projects = items.map((item) => { const slug = item.id.split("/")[2]!; const localeData = item[locale.value as "en"] ?? item.en; if (!localeData) { console.warn(`Missing '${locale.value}' for ${slug}`); } const { en: _en, fr: _fr, ...meta } = item; return { ...meta, id: slug.replace(/-([a-z])/g, (_: string, letter: string) => letter.toUpperCase(), ), description: localeData.description ?? "", summary: localeData.summary ?? "", tasks: localeData.tasks ?? [], }; }); this.loading = false; }, visitProject() { const link = this.projects[this.currentProject]?.link; if (link) { navigateTo(link, { open: { target: "_blank" } }); const achievements = useAchievementsStore(); achievements.unlock("projects_open_link"); } }, scrollProjects(direction: "left" | "right") { let offset = 0; if ( direction === "right" && this.currentProject < this.projects.length - 1 ) { this.currentProject += 1; offset = 104; } else if (direction === "left" && this.currentProject > 0) { this.currentProject -= 1; offset = -104; } if (offset !== 0) { gsap.fromTo( this, { offsetX: offset }, { offsetX: 0, duration: 0.1, ease: "none", }, ); } if (this.currentProject >= 4) { setTimeout(() => { const achievements = useAchievementsStore(); achievements.unlock("projects_view_5"); }, 500); } }, // TODO: not used anymore scrollToProject(index: number) { if (index === this.currentProject) return; const offset = (index - this.currentProject) * 69; this.currentProject = index; gsap.fromTo( this, { offsetX: offset }, { offsetX: 0, duration: 0.1, ease: "none", }, ); }, animateIntro() { this.isIntro = true; gsap.fromTo( this.intro, { fadeOpacity: 1 }, { delay: 3, fadeOpacity: 0, duration: 0.35, ease: "none", onComplete: () => { this.isIntro = false; }, }, ); }, animateOutro() { this.isOutro = true; gsap.fromTo( this.outro, { fadeOpacity: 0 }, { delay: 0.5, fadeOpacity: 1, duration: 0.35, ease: "none", onComplete: () => { setTimeout(() => { this.isOutro = false; const app = useAppStore(); app.navigateTo("home"); }, 3000); }, }, ); }, }, });