1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-19 13:19:41 +02:00

fix: Refactor Stores and Fix Missing Public Cookbooks (#5611)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2025-07-08 08:32:18 -05:00 committed by GitHub
parent 2f19d31d1b
commit aad7dc1abd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 96 additions and 45 deletions

View file

@ -138,14 +138,48 @@ export default defineNuxtComponent({
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
const cookbookPreferences = useCookbookPreferences(); const cookbookPreferences = useCookbookPreferences();
const { store: cookbooks, actions: cookbooksActions } = isOwnGroup.value ? useCookbookStore() : usePublicCookbookStore(groupSlug.value || "");
onMounted(() => { const ownCookbookStore = useCookbookStore(i18n);
if (!cookbooks.value.length) { const ownHouseholdStore = useHouseholdStore(i18n);
cookbooksActions.refresh();
const publicCookbookStoreCache = ref<Record<string, ReturnType<typeof usePublicCookbookStore>>>({});
const publicHouseholdStoreCache = ref<Record<string, ReturnType<typeof usePublicHouseholdStore>>>({});
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(() => { const householdsById = computed(() => {
return households.value.reduce((acc, household) => { return households.value.reduce((acc, household) => {
@ -178,6 +212,10 @@ export default defineNuxtComponent({
const currentUserHouseholdId = computed(() => $auth.user.value?.householdId); const currentUserHouseholdId = computed(() => $auth.user.value?.householdId);
const cookbookLinks = computed<SideBarLink[]>(() => { const cookbookLinks = computed<SideBarLink[]>(() => {
if (!cookbooks.value?.length) {
return [];
}
const sortedCookbooks = [...cookbooks.value].sort((a, b) => (a.position || 0) - (b.position || 0)); const sortedCookbooks = [...cookbooks.value].sort((a, b) => (a.position || 0) - (b.position || 0));
const ownLinks: SideBarLink[] = []; const ownLinks: SideBarLink[] = [];

View file

@ -1,4 +1,5 @@
import type { AxiosInstance, AxiosResponse, AxiosRequestConfig } from "axios"; import type { AxiosInstance, AxiosResponse, AxiosRequestConfig } from "axios";
import type { Composer } from "vue-i18n";
import type { ApiRequestInstance, RequestResponse } from "~/lib/api/types/non-generated"; import type { ApiRequestInstance, RequestResponse } from "~/lib/api/types/non-generated";
import { AdminAPI, PublicApi, UserApi } from "~/lib/api"; import { AdminAPI, PublicApi, UserApi } from "~/lib/api";
import { PublicExploreApi } from "~/lib/api/client-public"; import { PublicExploreApi } from "~/lib/api/client-public";
@ -52,31 +53,34 @@ function getRequests(axiosInstance: AxiosInstance): ApiRequestInstance {
}; };
} }
export const useRequests = function (): ApiRequestInstance { export const useRequests = function (i18n?: Composer): ApiRequestInstance {
const i18n = useI18n();
const { $axios } = useNuxtApp(); const { $axios } = useNuxtApp();
if (!i18n) {
// Only works in a setup block
i18n = useI18n();
}
$axios.defaults.headers.common["Accept-Language"] = i18n.locale.value; $axios.defaults.headers.common["Accept-Language"] = i18n.locale.value;
return getRequests($axios); return getRequests($axios);
}; };
export const useAdminApi = function (): AdminAPI { export const useAdminApi = function (i18n?: Composer): AdminAPI {
const requests = useRequests(); const requests = useRequests(i18n);
return new AdminAPI(requests); return new AdminAPI(requests);
}; };
export const useUserApi = function (): UserApi { export const useUserApi = function (i18n?: Composer): UserApi {
const requests = useRequests(); const requests = useRequests(i18n);
return new UserApi(requests); return new UserApi(requests);
}; };
export const usePublicApi = function (): PublicApi { export const usePublicApi = function (i18n?: Composer): PublicApi {
const requests = useRequests(); const requests = useRequests(i18n);
return new PublicApi(requests); return new PublicApi(requests);
}; };
export const usePublicExploreApi = function (groupSlug: string): PublicExploreApi { export const usePublicExploreApi = function (groupSlug: string, i18n?: Composer): PublicExploreApi {
const requests = useRequests(); const requests = useRequests(i18n);
return new PublicExploreApi(requests, groupSlug); return new PublicExploreApi(requests, groupSlug);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory";
import type { RecipeCategory } from "~/lib/api/types/recipe"; import type { RecipeCategory } from "~/lib/api/types/recipe";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -14,12 +15,12 @@ export const useCategoryData = function () {
}); });
}; };
export const useCategoryStore = function () { export const useCategoryStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<RecipeCategory>(store, loading, api.categories); return useStore<RecipeCategory>(store, loading, api.categories);
}; };
export const usePublicCategoryStore = function (groupSlug: string) { export const usePublicCategoryStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeCategory>(store, publicLoading, api.categories); return useReadOnlyStore<RecipeCategory>(store, publicLoading, api.categories);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useReadOnlyStore, useStore } from "../partials/use-store-factory"; import { useReadOnlyStore, useStore } from "../partials/use-store-factory";
import type { RecipeCookBook } from "~/lib/api/types/cookbook"; import type { RecipeCookBook } from "~/lib/api/types/cookbook";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -6,12 +7,12 @@ const store: Ref<RecipeCookBook[]> = ref([]);
const loading = ref(false); const loading = ref(false);
const publicLoading = ref(false); const publicLoading = ref(false);
export const useCookbookStore = function () { export const useCookbookStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<RecipeCookBook>(store, loading, api.cookbooks); return useStore<RecipeCookBook>(store, loading, api.cookbooks);
}; };
export const usePublicCookbookStore = function (groupSlug: string) { export const usePublicCookbookStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeCookBook>(store, publicLoading, api.cookbooks); return useReadOnlyStore<RecipeCookBook>(store, publicLoading, api.cookbooks);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory";
import type { IngredientFood } from "~/lib/api/types/recipe"; import type { IngredientFood } from "~/lib/api/types/recipe";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -15,12 +16,12 @@ export const useFoodData = function () {
}); });
}; };
export const useFoodStore = function () { export const useFoodStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<IngredientFood>(store, loading, api.foods); return useStore<IngredientFood>(store, loading, api.foods);
}; };
export const usePublicFoodStore = function (groupSlug: string) { export const usePublicFoodStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<IngredientFood>(store, publicLoading, api.foods); return useReadOnlyStore<IngredientFood>(store, publicLoading, api.foods);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useReadOnlyStore } from "../partials/use-store-factory"; import { useReadOnlyStore } from "../partials/use-store-factory";
import type { HouseholdSummary } from "~/lib/api/types/household"; import type { HouseholdSummary } from "~/lib/api/types/household";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -6,12 +7,12 @@ const store: Ref<HouseholdSummary[]> = ref([]);
const loading = ref(false); const loading = ref(false);
const publicLoading = ref(false); const publicLoading = ref(false);
export const useHouseholdStore = function () { export const useHouseholdStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useReadOnlyStore<HouseholdSummary>(store, loading, api.households); return useReadOnlyStore<HouseholdSummary>(store, loading, api.households);
}; };
export const usePublicHouseholdStore = function (groupSlug: string) { export const usePublicHouseholdStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<HouseholdSummary>(store, publicLoading, api.households); return useReadOnlyStore<HouseholdSummary>(store, publicLoading, api.households);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useStore } from "../partials/use-store-factory"; import { useData, useStore } from "../partials/use-store-factory";
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels"; import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import { useUserApi } from "~/composables/api"; import { useUserApi } from "~/composables/api";
@ -14,7 +15,7 @@ export const useLabelData = function () {
}); });
}; };
export const useLabelStore = function () { export const useLabelStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<MultiPurposeLabelOut>(store, loading, api.multiPurposeLabels); return useStore<MultiPurposeLabelOut>(store, loading, api.multiPurposeLabels);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory";
import type { RecipeTag } from "~/lib/api/types/recipe"; import type { RecipeTag } from "~/lib/api/types/recipe";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -14,12 +15,12 @@ export const useTagData = function () {
}); });
}; };
export const useTagStore = function () { export const useTagStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<RecipeTag>(store, loading, api.tags); return useStore<RecipeTag>(store, loading, api.tags);
}; };
export const usePublicTagStore = function (groupSlug: string) { export const usePublicTagStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeTag>(store, publicLoading, api.tags); return useReadOnlyStore<RecipeTag>(store, publicLoading, api.tags);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory"; import { useData, useReadOnlyStore, useStore } from "../partials/use-store-factory";
import type { RecipeTool } from "~/lib/api/types/recipe"; import type { RecipeTool } from "~/lib/api/types/recipe";
import { usePublicExploreApi, useUserApi } from "~/composables/api"; import { usePublicExploreApi, useUserApi } from "~/composables/api";
@ -20,12 +21,12 @@ export const useToolData = function () {
}); });
}; };
export const useToolStore = function () { export const useToolStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<RecipeTool>(store, loading, api.tools); return useStore<RecipeTool>(store, loading, api.tools);
}; };
export const usePublicToolStore = function (groupSlug: string) { export const usePublicToolStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug).explore; const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeTool>(store, publicLoading, api.tools); return useReadOnlyStore<RecipeTool>(store, publicLoading, api.tools);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useData, useStore } from "../partials/use-store-factory"; import { useData, useStore } from "../partials/use-store-factory";
import type { IngredientUnit } from "~/lib/api/types/recipe"; import type { IngredientUnit } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api"; import { useUserApi } from "~/composables/api";
@ -15,7 +16,7 @@ export const useUnitData = function () {
}); });
}; };
export const useUnitStore = function () { export const useUnitStore = function (i18n?: Composer) {
const api = useUserApi(); const api = useUserApi(i18n);
return useStore<IngredientUnit>(store, loading, api.units); return useStore<IngredientUnit>(store, loading, api.units);
}; };

View file

@ -1,3 +1,4 @@
import type { Composer } from "vue-i18n";
import { useReadOnlyStore } from "../partials/use-store-factory"; import { useReadOnlyStore } from "../partials/use-store-factory";
import { useRequests } from "../api/api-client"; import { useRequests } from "../api/api-client";
import type { UserSummary } from "~/lib/api/types/user"; import type { UserSummary } from "~/lib/api/types/user";
@ -11,8 +12,8 @@ class GroupUserAPIReadOnly extends BaseCRUDAPIReadOnly<UserSummary> {
itemRoute = (idOrUsername: string | number) => `/groups/members/${idOrUsername}`; itemRoute = (idOrUsername: string | number) => `/groups/members/${idOrUsername}`;
} }
export const useUserStore = function () { export const useUserStore = function (i18n?: Composer) {
const requests = useRequests(); const requests = useRequests(i18n);
const api = new GroupUserAPIReadOnly(requests); const api = new GroupUserAPIReadOnly(requests);
return useReadOnlyStore<UserSummary>(store, loading, api, { orderBy: "full_name" }); return useReadOnlyStore<UserSummary>(store, loading, api, { orderBy: "full_name" });