From aad7dc1abdc4b5694c4ee3129462d99d0475cac4 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Tue, 8 Jul 2025 08:32:18 -0500 Subject: [PATCH] fix: Refactor Stores and Fix Missing Public Cookbooks (#5611) Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com> --- frontend/components/Layout/DefaultLayout.vue | 48 +++++++++++++++++-- frontend/composables/api/api-client.ts | 24 ++++++---- .../composables/store/use-category-store.ts | 9 ++-- .../composables/store/use-cookbook-store.ts | 9 ++-- frontend/composables/store/use-food-store.ts | 9 ++-- .../composables/store/use-household-store.ts | 9 ++-- frontend/composables/store/use-label-store.ts | 5 +- frontend/composables/store/use-tag-store.ts | 9 ++-- frontend/composables/store/use-tool-store.ts | 9 ++-- frontend/composables/store/use-unit-store.ts | 5 +- frontend/composables/store/use-user-store.ts | 5 +- 11 files changed, 96 insertions(+), 45 deletions(-) diff --git a/frontend/components/Layout/DefaultLayout.vue b/frontend/components/Layout/DefaultLayout.vue index 12f0b0475..9f353680d 100644 --- a/frontend/components/Layout/DefaultLayout.vue +++ b/frontend/components/Layout/DefaultLayout.vue @@ -138,14 +138,48 @@ export default defineNuxtComponent({ const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); const cookbookPreferences = useCookbookPreferences(); - const { store: cookbooks, actions: cookbooksActions } = isOwnGroup.value ? useCookbookStore() : usePublicCookbookStore(groupSlug.value || ""); - onMounted(() => { - if (!cookbooks.value.length) { - cookbooksActions.refresh(); + + const ownCookbookStore = useCookbookStore(i18n); + const ownHouseholdStore = useHouseholdStore(i18n); + + const publicCookbookStoreCache = ref>>({}); + const publicHouseholdStoreCache = ref>>({}); + + function getPublicCookbookStore(slug: string) { + if (!publicCookbookStoreCache.value[slug]) { + publicCookbookStoreCache.value[slug] = usePublicCookbookStore(slug, i18n); } + return publicCookbookStoreCache.value[slug]; + } + + function getPublicHouseholdStore(slug: string) { + if (!publicHouseholdStoreCache.value[slug]) { + publicHouseholdStoreCache.value[slug] = usePublicHouseholdStore(slug, i18n); + } + return publicHouseholdStoreCache.value[slug]; + } + + const cookbooks = computed(() => { + if (isOwnGroup.value) { + return ownCookbookStore.store.value; + } + else if (groupSlug.value) { + const publicStore = getPublicCookbookStore(groupSlug.value); + return unref(publicStore.store); + } + return []; }); - const { store: households } = isOwnGroup.value ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value || ""); + const households = computed(() => { + if (isOwnGroup.value) { + return ownHouseholdStore.store.value; + } + else if (groupSlug.value) { + const publicStore = getPublicHouseholdStore(groupSlug.value); + return unref(publicStore.store); + } + return []; + }); const householdsById = computed(() => { return households.value.reduce((acc, household) => { @@ -178,6 +212,10 @@ export default defineNuxtComponent({ const currentUserHouseholdId = computed(() => $auth.user.value?.householdId); const cookbookLinks = computed(() => { + if (!cookbooks.value?.length) { + return []; + } + const sortedCookbooks = [...cookbooks.value].sort((a, b) => (a.position || 0) - (b.position || 0)); const ownLinks: SideBarLink[] = []; diff --git a/frontend/composables/api/api-client.ts b/frontend/composables/api/api-client.ts index 012d6c0f5..c130509fe 100644 --- a/frontend/composables/api/api-client.ts +++ b/frontend/composables/api/api-client.ts @@ -1,4 +1,5 @@ import type { AxiosInstance, AxiosResponse, AxiosRequestConfig } from "axios"; +import type { Composer } from "vue-i18n"; import type { ApiRequestInstance, RequestResponse } from "~/lib/api/types/non-generated"; import { AdminAPI, PublicApi, UserApi } from "~/lib/api"; import { PublicExploreApi } from "~/lib/api/client-public"; @@ -52,31 +53,34 @@ function getRequests(axiosInstance: AxiosInstance): ApiRequestInstance { }; } -export const useRequests = function (): ApiRequestInstance { - const i18n = useI18n(); +export const useRequests = function (i18n?: Composer): ApiRequestInstance { const { $axios } = useNuxtApp(); + if (!i18n) { + // Only works in a setup block + i18n = useI18n(); + } $axios.defaults.headers.common["Accept-Language"] = i18n.locale.value; return getRequests($axios); }; -export const useAdminApi = function (): AdminAPI { - const requests = useRequests(); +export const useAdminApi = function (i18n?: Composer): AdminAPI { + const requests = useRequests(i18n); return new AdminAPI(requests); }; -export const useUserApi = function (): UserApi { - const requests = useRequests(); +export const useUserApi = function (i18n?: Composer): UserApi { + const requests = useRequests(i18n); return new UserApi(requests); }; -export const usePublicApi = function (): PublicApi { - const requests = useRequests(); +export const usePublicApi = function (i18n?: Composer): PublicApi { + const requests = useRequests(i18n); return new PublicApi(requests); }; -export const usePublicExploreApi = function (groupSlug: string): PublicExploreApi { - const requests = useRequests(); +export const usePublicExploreApi = function (groupSlug: string, i18n?: Composer): PublicExploreApi { + const requests = useRequests(i18n); return new PublicExploreApi(requests, groupSlug); }; diff --git a/frontend/composables/store/use-category-store.ts b/frontend/composables/store/use-category-store.ts index 0509fa7c7..4e73fdfdc 100644 --- a/frontend/composables/store/use-category-store.ts +++ b/frontend/composables/store/use-category-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import type { RecipeCategory } from "~/lib/api/types/recipe"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -14,12 +15,12 @@ export const useCategoryData = function () { }); }; -export const useCategoryStore = function () { - const api = useUserApi(); +export const useCategoryStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.categories); }; -export const usePublicCategoryStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicCategoryStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.categories); }; diff --git a/frontend/composables/store/use-cookbook-store.ts b/frontend/composables/store/use-cookbook-store.ts index 34e7522a3..e3d4a5a81 100644 --- a/frontend/composables/store/use-cookbook-store.ts +++ b/frontend/composables/store/use-cookbook-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useReadOnlyStore, useStore } from "../partials/use-store-factory"; import type { RecipeCookBook } from "~/lib/api/types/cookbook"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -6,12 +7,12 @@ const store: Ref = ref([]); const loading = ref(false); const publicLoading = ref(false); -export const useCookbookStore = function () { - const api = useUserApi(); +export const useCookbookStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.cookbooks); }; -export const usePublicCookbookStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicCookbookStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.cookbooks); }; diff --git a/frontend/composables/store/use-food-store.ts b/frontend/composables/store/use-food-store.ts index 80ef1c7c3..1cb5c24a6 100644 --- a/frontend/composables/store/use-food-store.ts +++ b/frontend/composables/store/use-food-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import type { IngredientFood } from "~/lib/api/types/recipe"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -15,12 +16,12 @@ export const useFoodData = function () { }); }; -export const useFoodStore = function () { - const api = useUserApi(); +export const useFoodStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.foods); }; -export const usePublicFoodStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicFoodStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.foods); }; diff --git a/frontend/composables/store/use-household-store.ts b/frontend/composables/store/use-household-store.ts index 0622be677..3a702cab0 100644 --- a/frontend/composables/store/use-household-store.ts +++ b/frontend/composables/store/use-household-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useReadOnlyStore } from "../partials/use-store-factory"; import type { HouseholdSummary } from "~/lib/api/types/household"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -6,12 +7,12 @@ const store: Ref = ref([]); const loading = ref(false); const publicLoading = ref(false); -export const useHouseholdStore = function () { - const api = useUserApi(); +export const useHouseholdStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useReadOnlyStore(store, loading, api.households); }; -export const usePublicHouseholdStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicHouseholdStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.households); }; diff --git a/frontend/composables/store/use-label-store.ts b/frontend/composables/store/use-label-store.ts index 96219aa15..5345c7865 100644 --- a/frontend/composables/store/use-label-store.ts +++ b/frontend/composables/store/use-label-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useStore } from "../partials/use-store-factory"; import type { MultiPurposeLabelOut } from "~/lib/api/types/labels"; import { useUserApi } from "~/composables/api"; @@ -14,7 +15,7 @@ export const useLabelData = function () { }); }; -export const useLabelStore = function () { - const api = useUserApi(); +export const useLabelStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.multiPurposeLabels); }; diff --git a/frontend/composables/store/use-tag-store.ts b/frontend/composables/store/use-tag-store.ts index 939a14232..ef5777c34 100644 --- a/frontend/composables/store/use-tag-store.ts +++ b/frontend/composables/store/use-tag-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import type { RecipeTag } from "~/lib/api/types/recipe"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -14,12 +15,12 @@ export const useTagData = function () { }); }; -export const useTagStore = function () { - const api = useUserApi(); +export const useTagStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.tags); }; -export const usePublicTagStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicTagStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.tags); }; diff --git a/frontend/composables/store/use-tool-store.ts b/frontend/composables/store/use-tool-store.ts index 4886d0b13..092ff449a 100644 --- a/frontend/composables/store/use-tool-store.ts +++ b/frontend/composables/store/use-tool-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import type { RecipeTool } from "~/lib/api/types/recipe"; import { usePublicExploreApi, useUserApi } from "~/composables/api"; @@ -20,12 +21,12 @@ export const useToolData = function () { }); }; -export const useToolStore = function () { - const api = useUserApi(); +export const useToolStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.tools); }; -export const usePublicToolStore = function (groupSlug: string) { - const api = usePublicExploreApi(groupSlug).explore; +export const usePublicToolStore = function (groupSlug: string, i18n?: Composer) { + const api = usePublicExploreApi(groupSlug, i18n).explore; return useReadOnlyStore(store, publicLoading, api.tools); }; diff --git a/frontend/composables/store/use-unit-store.ts b/frontend/composables/store/use-unit-store.ts index 2ba3592d4..d9fea16d2 100644 --- a/frontend/composables/store/use-unit-store.ts +++ b/frontend/composables/store/use-unit-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useData, useStore } from "../partials/use-store-factory"; import type { IngredientUnit } from "~/lib/api/types/recipe"; import { useUserApi } from "~/composables/api"; @@ -15,7 +16,7 @@ export const useUnitData = function () { }); }; -export const useUnitStore = function () { - const api = useUserApi(); +export const useUnitStore = function (i18n?: Composer) { + const api = useUserApi(i18n); return useStore(store, loading, api.units); }; diff --git a/frontend/composables/store/use-user-store.ts b/frontend/composables/store/use-user-store.ts index b5ce6e385..44dbc5603 100644 --- a/frontend/composables/store/use-user-store.ts +++ b/frontend/composables/store/use-user-store.ts @@ -1,3 +1,4 @@ +import type { Composer } from "vue-i18n"; import { useReadOnlyStore } from "../partials/use-store-factory"; import { useRequests } from "../api/api-client"; import type { UserSummary } from "~/lib/api/types/user"; @@ -11,8 +12,8 @@ class GroupUserAPIReadOnly extends BaseCRUDAPIReadOnly { itemRoute = (idOrUsername: string | number) => `/groups/members/${idOrUsername}`; } -export const useUserStore = function () { - const requests = useRequests(); +export const useUserStore = function (i18n?: Composer) { + const requests = useRequests(i18n); const api = new GroupUserAPIReadOnly(requests); return useReadOnlyStore(store, loading, api, { orderBy: "full_name" });