Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 60 additions & 12 deletions client-app/app-runner.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createHead } from "@unhead/vue/client";
import { DefaultApolloClient } from "@vue/apollo-composable";
import { createApp, h, provide } from "vue";
import { apolloClient, getStore } from "@/core/api/graphql";
import { apolloClient, getPageContext } from "@/core/api/graphql";
import { GetSlugInfoDocument } from "@/core/api/graphql/types";
import { useCurrency, useThemeContext, useNavigations, useWhiteLabeling } from "@/core/composables";
import { useHotjar } from "@/core/composables/useHotjar";
import { useLanguages } from "@/core/composables/useLanguages";
Expand Down Expand Up @@ -35,7 +36,7 @@ import { templateBlocks } from "@/shared/static-content";
import { uiKit } from "@/ui-kit";
import { getLocales as getUIKitLocales } from "@/ui-kit/utilities/getLocales";
import App from "./App.vue";
import type { StoreResponseType } from "./core/api/graphql/types";
import type { PageContextResponseType } from "./core/api/graphql/types";

// eslint-disable-next-line no-restricted-exports
export default async () => {
Expand Down Expand Up @@ -66,7 +67,7 @@ export default async () => {

app.use(authPlugin);

const { fetchUser, user, isAuthenticated } = useUser();
const { setUser, user, isAuthenticated, savedUserId } = useUser();
const { themeContext, addPresetToThemeContext, setThemeContext } = useThemeContext();
const {
currentLanguage,
Expand All @@ -76,11 +77,13 @@ export default async () => {
fetchLocaleMessages,
mergeLocalesMessages,
resolveLocale,
getUrlWithoutPossibleLocale,
resolvePossibleLocale,
} = useLanguages();
const { currentCurrency } = useCurrency();
const { init: initializeHotjar } = useHotjar();
const { fetchCatalogMenu } = useNavigations();
const { themePresetName, fetchWhiteLabelingSettings } = useWhiteLabeling();
const { themePresetName, setWhiteLabelingSettings } = useWhiteLabeling();

const fallback = {
locale: FALLBACK_LOCALE,
Expand All @@ -90,18 +93,41 @@ export default async () => {
},
};

const storePromise = getStore(
IS_DEVELOPMENT ? extractHostname(import.meta.env.APP_BACKEND_URL as string) : window.location.hostname,
) as Promise<StoreResponseType>;
// get initialization query parameters
const tempRouter = createRouter({ base: "" });
const initialRoute = tempRouter.resolve(window.location.pathname + window.location.search + window.location.hash);
let permalink = initialRoute.path;
const possibleCultureName = resolvePossibleLocale(permalink);
permalink = getPermalink(permalink, getUrlWithoutPossibleLocale);

const [store] = await Promise.all([storePromise, fetchUser(), fallback.setMessage()]);
const domain = IS_DEVELOPMENT ? extractHostname(import.meta.env.APP_BACKEND_URL as string) : window.location.hostname;
const userId = savedUserId.value;

const getPageContextPromise = getPageContext({
domain: domain,
userId: userId,
permalink: permalink,
cultureName: possibleCultureName,
}) as Promise<PageContextResponseType>;

const [pageContext] = await Promise.all([getPageContextPromise, fallback.setMessage()]);

const store = pageContext.store;
const userResult = pageContext.user;
const whiteLabelingSetting = pageContext.whiteLabelingSettings;

if (!store) {
alert("Related store not found. Please contact your site administrator.");
throw new Error("Store not found. Check graphql request, GetStore query");
throw new Error("Store not found. Check graphql request, PageContext query");
}

if (!userResult) {
alert("Error fetching user. Please contact your site administrator.");
throw new Error("Error fetching user. Check graphql request, PageContext query");
}

setThemeContext(store);
setUser(userResult);

/**
* Creating plugin instances
Expand Down Expand Up @@ -131,11 +157,29 @@ export default async () => {
currencyCode: currentCurrency.value.code,
});

// Seed Apollo cache with initial slugInfo from pageContext to avoid the first network call
try {
const baseVariables = {
userId: user.value.id,
storeId: themeContext.value.storeId,
cultureName: currentLanguage.value.cultureName,
} as const;

// Seed cache only for the normalized permalink ("/" for home, otherwise without leading slash)
apolloClient.writeQuery({
query: GetSlugInfoDocument,
variables: { ...baseVariables, permalink },
data: { slugInfo: pageContext.slugInfo },
});
} catch (e) {
Logger.warn("Failed to seed slugInfo into Apollo cache", e as Error);
}

/**
* Other settings
*/

await fetchWhiteLabelingSettings();
setWhiteLabelingSettings(whiteLabelingSetting);
addPresetToThemeContext(themePresetName.value ?? themeContext.value.defaultPresetName);

if (isAuthenticated.value || themeContext.value.storeSettings.anonymousUsersAllowed) {
Expand Down Expand Up @@ -194,8 +238,6 @@ export default async () => {
// Register Page builder product components globally
Object.entries(ProductBlocks).forEach(([name, component]) => app.component(name, component));

await router.isReady();

app.config.warnHandler = (msg, _, trace) => {
// to remove builder.io warnings
if (consoleIgnoredErrors.some((err) => msg.includes(err) && trace.includes(BUILDER_IO_TRACE_MARKER))) {
Expand All @@ -207,3 +249,9 @@ export default async () => {

app.mount(appElement);
};

function getPermalink(permalink: string, getUrlWithoutPossibleLocale: (fullPath: string) => string) {
permalink = getUrlWithoutPossibleLocale(permalink);
permalink = permalink === "/" ? "/" : permalink.replace(/^\/+/, "");
return permalink;
}
15 changes: 15 additions & 0 deletions client-app/core/api/graphql/fragments/slugInfoFields.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fragment slugInfoResponseTypeFields on SlugInfoResponseType {
entityInfo {
id
isActive
languageCode
objectId
objectType
semanticUrl
metaDescription
metaKeywords
pageTitle
outline
}
redirectUrl
}
48 changes: 48 additions & 0 deletions client-app/core/api/graphql/fragments/storeFields.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#import "./fragments/allCurrencyFields.graphql"
#import "./fragments/allLanguageFields.graphql"

fragment storeResponseTypeFields on StoreResponseType {
storeId
storeName
catalogId
storeUrl
defaultLanguage {
...allLanguageFields
}
availableLanguages {
...allLanguageFields
}
defaultCurrency {
...allCurrencyFields
}
availableCurrencies {
...allCurrencyFields
}
settings {
authenticationTypes
subscriptionEnabled
taxCalculationEnabled
anonymousUsersAllowed
environmentName
emailVerificationEnabled
emailVerificationRequired
createAnonymousOrderEnabled
seoLinkType
defaultSelectedForCheckout
passwordRequirements {
requireLowercase
requireUppercase
requireDigit
requiredLength
requiredUniqueChars
requireNonAlphanumeric
}
modules {
moduleId
settings {
name
value
}
}
}
}
40 changes: 40 additions & 0 deletions client-app/core/api/graphql/fragments/userFields.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
fragment userTypeFields on UserType {
id
memberId
userName
email
emailConfirmed
photoUrl
phoneNumber
permissions
isAdministrator
passwordExpired
passwordExpiryInDays
forcePasswordChange
lockedState
contact {
id
firstName
lastName
fullName
organizationId
defaultLanguage
currencyCode
selectedAddressId
organizations {
items {
id
name
}
}
}
operator {
userName
contact {
fullName
}
}
roles {
name
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
fragment whiteLabelingFields on WhiteLabelingSettingsType {
logoUrl
secondaryLogoUrl
themePresetName
isOrganizationLogoUploaded
favicons {
rel
type
sizes
href
}
footerLinks {
title
url
priority
childItems {
title
url
priority
}
}
}
1 change: 1 addition & 0 deletions client-app/core/api/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./fulfillmentCenters";
export * from "./orders";
export * from "./organization";
export * from "./page";
export * from "./pageContext";
export * from "./payment";
export * from "./slugInfo";
export * from "./store";
Expand Down
1 change: 1 addition & 0 deletions client-app/core/api/graphql/pageContext/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./queries/getPageContext";
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#import "../../../fragments/storeFields.graphql"
#import "../../../fragments/slugInfoFields.graphql"
#import "../../../fragments/userFields.graphql"
#import "../../../fragments/whiteLabelingFields.graphql"

query GetPageContext(
$userId: String
$organizationId: String
$domain: String
$storeId: String
$permalink: String
$cultureName: String
) {
pageContext(
userId: $userId
organizationId: $organizationId
domain: $domain
storeId: $storeId
permalink: $permalink
cultureName: $cultureName
) {
user {
...userTypeFields
}
store {
...storeResponseTypeFields
}
slugInfo {
...slugInfoResponseTypeFields
}
whiteLabelingSettings {
...whiteLabelingFields
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { GetPageContextDocument } from "@/core/api/graphql/types";
import { graphqlClient } from "../../../client";
import type { GetPageContextQueryVariables } from "@/core/api/graphql/types";

export async function getPageContext(payload: GetPageContextQueryVariables) {
const { data } = await graphqlClient.query({
query: GetPageContextDocument,
variables: payload,
});

return data.pageContext;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import type { QuerySlugInfoArgs } from "@/core/api/graphql/types";
import type { MaybeRefOrGetter } from "vue";

export function useGetSlugInfo(payload: MaybeRefOrGetter<QuerySlugInfoArgs>) {
return useQuery(GetSlugInfoDocument, payload, { fetchPolicy: "cache-and-network" });
return useQuery(GetSlugInfoDocument, payload, { fetchPolicy: "cache-first" });
}
Loading
Loading