function createProductActivities({ productsContentState, opportunitiesQueryState, loadCrm, loadActions, blockActions, render }) { let cycle = null; function begin(requestId, token, opportunityNumbers = null) { cycle = { requestId, token, numbers: opportunityNumbers && new Set(opportunityNumbers.map(String)), version: opportunitiesQueryState.periodRequestVersion, completed: false }; } function complete(requestId) { const current = cycle; if (!current || current.completed || current.requestId !== requestId || productsContentState.currentProductsRequestId !== requestId) return; current.completed = true; const entries = [...productsContentState.opptyProductsByNumber].filter(([number]) => !current.numbers || current.numbers.has(String(number))); const products = entries.map(([, result]) => result).filter(result => !result.error); if (!products.length) return; const cachedAt = Math.max(...products.map(result => Number(result.cachedAt) || 0)); const isActive = () => cycle === current && productsContentState.currentProductsRequestId === requestId && opportunitiesQueryState.periodRequestVersion === current.version; // Product cache hits do not guarantee that the corresponding activity cache exists. return Promise.resolve(loadCrm(current.token, current.version, { cachedAt, cacheOnly: false })).then(async success => { if (!isActive()) return; if (success === false) { products.forEach(blockActions); render(); return; } const pending = []; const seen = new Set(); for (const result of products) { const items = result.items.filter(item => { const id = String(item?.RevnId ?? ""); if (seen.has(id)) return false; seen.add(id); return true; }); pending.push(loadActions({ ...result, items }, true, { cachedAt: result.cachedAt, cacheOnly: false })); } await Promise.allSettled(pending); if (!isActive()) return; if (cachedAt > 0) productsContentState.productsLastCachedAt = cachedAt; render(); }).catch(() => { if (isActive()) { products.forEach(blockActions); render(); } }); } return { begin, complete }; }