From a5c1c932606cdf3863664e15465b487114c7994a Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Wed, 26 Nov 2025 22:58:03 +0100 Subject: [PATCH] feat(settings): more specific notification handling, and put menu in query to allow user to go back in history to go back in menus --- .../Settings/BottomScreen/BottomScreen.vue | 37 +---- .../BottomScreen/Menus/Clock/Menu.vue | 2 +- .../Settings/BottomScreen/Menus/Menus.vue | 70 ++++++++-- .../BottomScreen/Menus/Options/Menu.vue | 2 +- .../Settings/BottomScreen/Menus/User/Menu.vue | 2 +- .../Settings/TopScreen/Notifications.vue | 127 +++++++++++------- app/composables/useMenuAnimation.ts | 36 +++-- app/stores/settings.ts | 27 ++-- i18n/locales/en.json | 6 +- 9 files changed, 181 insertions(+), 128 deletions(-) diff --git a/app/components/Settings/BottomScreen/BottomScreen.vue b/app/components/Settings/BottomScreen/BottomScreen.vue index 5391dc4..1c4079b 100644 --- a/app/components/Settings/BottomScreen/BottomScreen.vue +++ b/app/components/Settings/BottomScreen/BottomScreen.vue @@ -1,44 +1,9 @@ diff --git a/app/components/Settings/BottomScreen/Menus/Clock/Menu.vue b/app/components/Settings/BottomScreen/Menus/Clock/Menu.vue index 004662d..3bccb0b 100644 --- a/app/components/Settings/BottomScreen/Menus/Clock/Menu.vue +++ b/app/components/Settings/BottomScreen/Menus/Clock/Menu.vue @@ -34,7 +34,7 @@ const isAnyOtherMenuOpen = computed(() => settingsStore.isAnyOtherMenuOpen("clock"), ); -const animation = useMenuAnimation(isOpen); +const animation = useMenuAnimation("clock", isOpen); useRender((ctx) => { ctx.translate(props.x, props.y); diff --git a/app/components/Settings/BottomScreen/Menus/Menus.vue b/app/components/Settings/BottomScreen/Menus/Menus.vue index 3b92df8..f1f8962 100644 --- a/app/components/Settings/BottomScreen/Menus/Menus.vue +++ b/app/components/Settings/BottomScreen/Menus/Menus.vue @@ -1,10 +1,16 @@ diff --git a/app/components/Settings/BottomScreen/Menus/Options/Menu.vue b/app/components/Settings/BottomScreen/Menus/Options/Menu.vue index 21d86ac..cce21df 100644 --- a/app/components/Settings/BottomScreen/Menus/Options/Menu.vue +++ b/app/components/Settings/BottomScreen/Menus/Options/Menu.vue @@ -34,7 +34,7 @@ const isAnyOtherMenuOpen = computed(() => settingsStore.isAnyOtherMenuOpen("options"), ); -const animation = useMenuAnimation(isOpen); +const animation = useMenuAnimation("options", isOpen); useRender((ctx) => { ctx.translate(props.x, props.y); diff --git a/app/components/Settings/BottomScreen/Menus/User/Menu.vue b/app/components/Settings/BottomScreen/Menus/User/Menu.vue index 52ea109..d072d0b 100644 --- a/app/components/Settings/BottomScreen/Menus/User/Menu.vue +++ b/app/components/Settings/BottomScreen/Menus/User/Menu.vue @@ -37,7 +37,7 @@ const isAnyOtherMenuOpen = computed(() => settingsStore.isAnyOtherMenuOpen("user"), ); -const animation = useMenuAnimation(isOpen); +const animation = useMenuAnimation("user", isOpen); useRender((ctx) => { ctx.translate(props.x, props.y); diff --git a/app/components/Settings/TopScreen/Notifications.vue b/app/components/Settings/TopScreen/Notifications.vue index eb902e4..5c6c080 100644 --- a/app/components/Settings/TopScreen/Notifications.vue +++ b/app/components/Settings/TopScreen/Notifications.vue @@ -33,25 +33,6 @@ const [ GBA_MODE_IMAGE, ); -const activeMenu = computed(() => { - if (!store.activeMenu) return null; - - if (/^options[A-Z]/.test(store.activeMenu)) { - return { id: "options", image: optionsImage }; - } - if (/^clock[A-Z]/.test(store.activeMenu)) { - return { id: "clock", image: clockImage }; - } - if (/^user[A-Z]/.test(store.activeMenu)) { - return { id: "user", image: userImage }; - } - if (/^touchScreen[A-Z]/.test(store.activeMenu)) { - return { id: "touchScreen", image: touchScreenImage }; - } - - return null; -}); - const renderNotification = ( ctx: CanvasRenderingContext2D, image: HTMLImageElement, @@ -73,50 +54,100 @@ const renderNotification = ( } }; -const notificationStack = computed(() => { - const stack: Array<{ id: string; image: HTMLImageElement }> = [ - { id: "main", image: settingsImage! }, - ]; +const mainNotification = computed(() => ({ + image: settingsImage!, + title: $t("settings.title"), + description: $t("settings.description"), +})); - if (activeMenu.value) { - stack.push({ id: activeMenu.value.id, image: activeMenu.value.image! }); +const menuNotification = computed(() => { + if (!store.currentMenu) return null; + + let image: HTMLImageElement | null = null; + let id = ""; + + if (/^options[A-Z]/.test(store.currentMenu)) { + image = optionsImage!; + id = "options"; + } else if (/^clock[A-Z]/.test(store.currentMenu)) { + image = clockImage!; + id = "clock"; + } else if (/^user[A-Z]/.test(store.currentMenu)) { + image = userImage!; + id = "user"; + } else if (/^touchScreen[A-Z]/.test(store.currentMenu)) { + image = touchScreenImage!; + id = "touchScreen"; } - for (const view of store.navigationStack) { - const menuMatch = view.match(/^(options|clock|user|touchScreen)(.+)$/); - if (menuMatch) { - const [, menu, submenu] = menuMatch; - const submenuKey = submenu!.charAt(0).toLowerCase() + submenu!.slice(1); - const imageMap: Record = { - optionsStartUp: startUpImage!, - optionsLanguage: languageImage!, - optionsGbaMode: gbaModeImage!, - }; - if (imageMap[view]) { - stack.push({ id: `${menu}.${submenuKey}`, image: imageMap[view]! }); - } - } - } + if (!image) return null; - return stack; + return { + image, + title: $t(`settings.${id}.title`), + description: $t(`settings.${id}.description`), + }; +}); + +const IMAGES_MAP: Record = { + optionsStartUp: startUpImage!, + optionsLanguage: languageImage!, + optionsGbaMode: gbaModeImage!, +}; + +const submenuNotification = computed(() => { + if (!store.currentSubMenu) return null; + + const image = IMAGES_MAP[store.currentSubMenu]; + if (!image) return null; + + const menuMatch = store.currentSubMenu.match( + /^(options|clock|user|touchScreen)(.+)$/, + ); + if (!menuMatch) return null; + + const [, menu, submenu] = menuMatch; + const submenuKey = submenu!.charAt(0).toLowerCase() + submenu!.slice(1); + + return { + image, + title: $t(`settings.${menu}.${submenuKey}.title`), + description: $t(`settings.${menu}.${submenuKey}.description`), + }; }); useRender((ctx) => { - const stackSize = notificationStack.value.length; + let count = 1; + if (menuNotification.value) count++; + if (submenuNotification.value) count++; - ctx.translate(0, 144 - (stackSize - 1) * 16); + ctx.translate(0, 144 - (count - 1) * 16); - for (let i = 0; i < stackSize; i++) { - const notification = notificationStack.value[i]!; + renderNotification( + ctx, + mainNotification.value.image, + mainNotification.value.title, + mainNotification.value.description, + ); + if (menuNotification.value) { + ctx.translate(0, 16); renderNotification( ctx, - notification.image, - $t(`settings.${notification.id}.title`), - $t(`settings.${notification.id}.description`), + menuNotification.value.image, + menuNotification.value.title, + menuNotification.value.description, ); + } + if (submenuNotification.value) { ctx.translate(0, 16); + renderNotification( + ctx, + submenuNotification.value.image, + submenuNotification.value.title, + submenuNotification.value.description, + ); } }); diff --git a/app/composables/useMenuAnimation.ts b/app/composables/useMenuAnimation.ts index e15ccfb..f521a9b 100644 --- a/app/composables/useMenuAnimation.ts +++ b/app/composables/useMenuAnimation.ts @@ -1,33 +1,49 @@ import gsap from "gsap"; -export const useMenuAnimation = (isOpen: Ref) => { - const animation = reactive({ +export const useMenuAnimation = (key: string, isOpen: Ref) => { + const animation = useState(`animation-${key}`, () => ({ playing: false, stage1Offset: 48, stage2Offset: 48, - }); + })); watch(isOpen, (current, previous) => { const duration = 0.1; const timeline = gsap.timeline({ onStart: () => { - animation.playing = true; + animation.value.playing = true; }, onComplete: () => { - animation.playing = false; + animation.value.playing = false; }, }); if (current === true && previous === false) { timeline - .fromTo(animation, { stage1Offset: 48 }, { stage1Offset: 0, duration }) - .fromTo(animation, { stage2Offset: 48 }, { stage2Offset: 0, duration }); + .fromTo( + animation.value, + { stage1Offset: 48 }, + { stage1Offset: 0, duration }, + ) + .fromTo( + animation.value, + { stage2Offset: 48 }, + { stage2Offset: 0, duration }, + ); } else if (current === false && previous === true) { timeline - .fromTo(animation, { stage2Offset: 0 }, { stage2Offset: 48, duration }) - .fromTo(animation, { stage1Offset: 0 }, { stage1Offset: 48, duration }); + .fromTo( + animation.value, + { stage2Offset: 0 }, + { stage2Offset: 48, duration }, + ) + .fromTo( + animation.value, + { stage1Offset: 0 }, + { stage1Offset: 48, duration }, + ); } }); - return animation; + return animation.value; }; diff --git a/app/stores/settings.ts b/app/stores/settings.ts index d3327b9..bef5c8d 100644 --- a/app/stores/settings.ts +++ b/app/stores/settings.ts @@ -1,37 +1,28 @@ export const useSettingsStore = defineStore("settings", { state: () => ({ - activeMenu: null as string | null, - navigationStack: [] as string[], + currentMenu: null as string | null, + currentSubMenu: null as string | null, }), getters: { isMenuOpen: (state) => (menu: string) => { - if (!state.activeMenu) return false; - return new RegExp(`^${menu}[A-Z]`).test(state.activeMenu); + if (!state.currentMenu) return false; + return new RegExp(`^${menu}[A-Z]`).test(state.currentMenu); }, isAnyOtherMenuOpen: (state) => (excludeMenu: string) => { - if (!state.activeMenu) return false; + if (!state.currentMenu) return false; return ["options", "clock", "user", "touchScreen"] .filter((m) => m !== excludeMenu) - .some((m) => new RegExp(`^${m}[A-Z]`).test(state.activeMenu!)); - }, - - currentView: (state) => { - if (state.navigationStack.length === 0) return "menu"; - return state.navigationStack[state.navigationStack.length - 1]; + .some((m) => new RegExp(`^${m}[A-Z]`).test(state.currentMenu!)); }, }, actions: { setActiveMenu(menu: string | null) { - this.activeMenu = menu; + this.currentMenu = menu; }, - pushNavigation(view: string) { - this.navigationStack.push(view); - }, - - popNavigation() { - this.navigationStack.pop(); + setCurrentSubMenu(submenu: string | null) { + this.currentSubMenu = submenu; }, }, }); diff --git a/i18n/locales/en.json b/i18n/locales/en.json index a6d5e1b..f753ec6 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -1,9 +1,7 @@ { "settings": { - "main": { - "title": "Settings", - "description": "Change system settings here. Select\nthe settings you'd like to change." - }, + "title": "Settings", + "description": "Change system settings here. Select\nthe settings you'd like to change.", "options": { "title": "Options",