mirror of
https://github.com/FoggedLens/deflock.git
synced 2026-07-14 15:57:20 +02:00
Fix cart/collection duplication and reduce empty-cart risk in Shopify embed
Root causes: - updateConfig() on collection switches did not clear previously-rendered product tiles before adding the new collection's, so switching categories stacked the new grid below the old one instead of replacing it - Theme toggles could fire overlapping async onReady() callbacks (if isDark settles in more than one tick), and the second createComponent() call landed on top of the first instead of replacing it, producing duplicate tiles - buildClient() was being called fresh on every theme/collection change instead of once per page load, increasing exposure to the pre-existing "empty cart on page load" issue by triggering it on every toggle Fixes: - Add getShopifyUI() to bootstrap buildClient()/UI.onReady() exactly once and cache the promise; every render reuses the same client/UI going forward, matching original page-load behavior - Remove the updateConfig() shortcut entirely — collection switches and theme changes now go through the same single render path - Always destroyShopifyComponents() (collection plus any auto-spawned cart/toggle) and clear the container before rebuilding, so no stale DOM can persist across a switch - Reintroduce a shopifyRenderToken guard so a stale async callback from a superseded render becomes a no-op instead of creating duplicate tiles Files changed: src/pages/StorePage.vue
This commit is contained in:
+59
-30
@@ -523,52 +523,80 @@ declare global {
|
||||
interface Window { ShopifyBuy: any }
|
||||
}
|
||||
|
||||
// The cart drawer and product modal are singletons the SDK attaches outside
|
||||
// `shopifyContainer` (not as children of it), tied to the client for the
|
||||
// lifetime of the page. Destroying and recreating the collection component
|
||||
// on every theme change only rebuilds the product grid — the cart/modal
|
||||
// singletons are untouched and stay stuck on whatever theme was active the
|
||||
// first time they were created. Keeping a reference and calling
|
||||
// updateConfig() instead pushes new styles into all of them in place.
|
||||
// The client/checkout is built exactly once and reused for the lifetime of
|
||||
// the page. Rebuilding it on every theme or collection change risked
|
||||
// re-triggering whatever caused the earlier "empty cart on page load" bug —
|
||||
// now on every toggle instead of just once per load.
|
||||
let shopifyUIPromise: Promise<any> | null = null;
|
||||
let shopifyUI: any = null;
|
||||
let shopifyComponent: any = null;
|
||||
let shopifyRenderToken = 0;
|
||||
|
||||
function renderShopify(id: string) {
|
||||
const container = shopifyContainer.value;
|
||||
if (!container || !window.ShopifyBuy?.UI) return;
|
||||
|
||||
const options = buildShopifyOptions(isDark.value);
|
||||
|
||||
if (shopifyComponent?.updateConfig) {
|
||||
shopifyComponent.updateConfig({
|
||||
id,
|
||||
node: container,
|
||||
moneyFormat: '%24%7B%7Bamount%7D%7D',
|
||||
options,
|
||||
});
|
||||
shopifyReady.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
shopifyReady.value = false;
|
||||
function getShopifyUI(): Promise<any> {
|
||||
if (shopifyUIPromise) return shopifyUIPromise;
|
||||
const client = window.ShopifyBuy.buildClient({
|
||||
domain: 'agora.markets',
|
||||
storefrontAccessToken: '78991208f7fea14aa4ac02a58f8025dd',
|
||||
});
|
||||
window.ShopifyBuy.UI.onReady(client).then((ui: any) => {
|
||||
shopifyUIPromise = window.ShopifyBuy.UI.onReady(client).then((ui: any) => {
|
||||
shopifyUI = ui;
|
||||
return ui;
|
||||
});
|
||||
return shopifyUIPromise;
|
||||
}
|
||||
|
||||
// The cart drawer and product modal are singletons the SDK attaches outside
|
||||
// `shopifyContainer` (not as children of it). Destroys every component
|
||||
// instance the SDK created (collection, plus whatever cart/toggle it
|
||||
// auto-spawned) so a rebuild always starts from a clean slate — updateConfig()
|
||||
// was tried here previously but doesn't reliably clear already-mounted DOM,
|
||||
// which caused old collections/themes to linger alongside new ones.
|
||||
function destroyShopifyComponents() {
|
||||
if (!shopifyUI?.components) return;
|
||||
Object.values(shopifyUI.components).forEach((instances: any) => {
|
||||
(instances as any[]).forEach((component) => {
|
||||
try {
|
||||
component?.destroy?.();
|
||||
} catch {
|
||||
// Component may already be detached; safe to ignore.
|
||||
}
|
||||
});
|
||||
});
|
||||
shopifyComponent = null;
|
||||
}
|
||||
|
||||
function renderShopify(id: string) {
|
||||
const container = shopifyContainer.value;
|
||||
if (!container) return;
|
||||
|
||||
// Guards against overlapping renders (e.g. the theme value settling in
|
||||
// two quick ticks). If a newer render starts before this one's async
|
||||
// onReady() resolves, the stale callback below becomes a no-op instead
|
||||
// of creating a second, duplicate set of product tiles.
|
||||
const myToken = ++shopifyRenderToken;
|
||||
shopifyReady.value = false;
|
||||
|
||||
getShopifyUI().then((ui: any) => {
|
||||
if (myToken !== shopifyRenderToken) return;
|
||||
|
||||
destroyShopifyComponents();
|
||||
container.innerHTML = '';
|
||||
|
||||
shopifyComponent = ui.createComponent('collection', {
|
||||
id,
|
||||
node: container,
|
||||
moneyFormat: '%24%7B%7Bamount%7D%7D',
|
||||
options,
|
||||
options: buildShopifyOptions(isDark.value),
|
||||
});
|
||||
shopifyReady.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Push new styles into the existing component whenever the Vuetify theme
|
||||
// flips, instead of tearing down and rebuilding the whole widget.
|
||||
// Rebuild the widget whenever the Vuetify theme flips, so the cart drawer
|
||||
// and product modal singletons pick up the new theme immediately instead
|
||||
// of requiring a page refresh.
|
||||
watch(isDark, () => {
|
||||
renderShopify(collectionId.value);
|
||||
if (window.ShopifyBuy?.UI) renderShopify(collectionId.value);
|
||||
});
|
||||
|
||||
function loadShopifySDK() {
|
||||
@@ -583,6 +611,7 @@ function loadShopifySDK() {
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
|
||||
// ── Printables ──────────────────────────────────────────────────────────────────
|
||||
|
||||
interface Printable {
|
||||
|
||||
Reference in New Issue
Block a user