ng-miam 10.5.11 → 10.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1646,6 +1646,202 @@
1646
1646
  analyticsEnabled: true // Only used in DEV mode
1647
1647
  };
1648
1648
 
1649
+ var WINDOW_SINGLETON_KEY = '__mealzRecipeCardShowTracker__';
1650
+ /**
1651
+ * Tracks recipe.show events and prevents duplicates (first show, or again after scroll).
1652
+ * Shared across SDK and Lit recipe cards via {@link WINDOW_SINGLETON_KEY} on window.
1653
+ */
1654
+ var RecipeCardShowTracker = /** @class */ (function () {
1655
+ function RecipeCardShowTracker() {
1656
+ var _this = this;
1657
+ this.trackedRecipes = new Map();
1658
+ this.hasScrolled = false;
1659
+ this.scrollListener = null;
1660
+ this.cleanupInterval = null;
1661
+ this.MAX_RECIPES = 1000;
1662
+ this.CLEANUP_INTERVAL = 5 * 60 * 1000;
1663
+ this.handlePageUnload = function () {
1664
+ _this.destroy();
1665
+ };
1666
+ this.setupScrollListener();
1667
+ this.startAutoCleanup();
1668
+ this.setupPageUnloadListener();
1669
+ }
1670
+ RecipeCardShowTracker.getInstance = function () {
1671
+ var w = typeof window !== 'undefined'
1672
+ ? window
1673
+ : undefined;
1674
+ var fromWindow = w === null || w === void 0 ? void 0 : w[WINDOW_SINGLETON_KEY];
1675
+ if (fromWindow) {
1676
+ RecipeCardShowTracker.instance = fromWindow;
1677
+ return fromWindow;
1678
+ }
1679
+ if (!RecipeCardShowTracker.instance) {
1680
+ RecipeCardShowTracker.instance = new RecipeCardShowTracker();
1681
+ if (w) {
1682
+ w[WINDOW_SINGLETON_KEY] = RecipeCardShowTracker.instance;
1683
+ }
1684
+ }
1685
+ return RecipeCardShowTracker.instance;
1686
+ };
1687
+ RecipeCardShowTracker.prototype.trackRecipeShow = function (recipeId, analyticsPath, categoryId) {
1688
+ var _a, _b;
1689
+ var now = Date.now();
1690
+ var lastShowTime = this.trackedRecipes.get(recipeId);
1691
+ var isFirstShow = !lastShowTime;
1692
+ var hasScrolledSinceLastShow = this.hasScrolled;
1693
+ if (!isFirstShow && !hasScrolledSinceLastShow) {
1694
+ return;
1695
+ }
1696
+ if (this.trackedRecipes.size >= this.MAX_RECIPES) {
1697
+ this.cleanOldRecipes();
1698
+ }
1699
+ this.trackedRecipes.set(recipeId, now);
1700
+ (_b = (_a = window.mealzInternal) === null || _a === void 0 ? void 0 : _a.analytics) === null || _b === void 0 ? void 0 : _b.sendEvent('recipe.show', analyticsPath, {
1701
+ recipe_id: recipeId,
1702
+ category_id: categoryId
1703
+ });
1704
+ };
1705
+ RecipeCardShowTracker.prototype.cleanOldRecipes = function () {
1706
+ var e_1, _c;
1707
+ var now = Date.now();
1708
+ var oneHourAgo = now - (60 * 60 * 1000);
1709
+ try {
1710
+ for (var _d = __values(this.trackedRecipes.entries()), _e = _d.next(); !_e.done; _e = _d.next()) {
1711
+ var _f = __read(_e.value, 2), recipeId = _f[0], timestamp = _f[1];
1712
+ if (timestamp < oneHourAgo) {
1713
+ this.trackedRecipes.delete(recipeId);
1714
+ }
1715
+ }
1716
+ }
1717
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
1718
+ finally {
1719
+ try {
1720
+ if (_e && !_e.done && (_c = _d.return)) _c.call(_d);
1721
+ }
1722
+ finally { if (e_1) throw e_1.error; }
1723
+ }
1724
+ };
1725
+ RecipeCardShowTracker.prototype.clearAllRecipes = function () {
1726
+ this.trackedRecipes.clear();
1727
+ };
1728
+ RecipeCardShowTracker.prototype.destroy = function () {
1729
+ if (this.scrollListener) {
1730
+ document.removeEventListener('scroll', this.scrollListener, true);
1731
+ this.scrollListener = null;
1732
+ }
1733
+ window.removeEventListener('beforeunload', this.handlePageUnload);
1734
+ if (this.cleanupInterval) {
1735
+ clearInterval(this.cleanupInterval);
1736
+ this.cleanupInterval = null;
1737
+ }
1738
+ this.trackedRecipes.clear();
1739
+ this.hasScrolled = false;
1740
+ RecipeCardShowTracker.instance = null;
1741
+ if (typeof window !== 'undefined') {
1742
+ delete window[WINDOW_SINGLETON_KEY];
1743
+ }
1744
+ };
1745
+ RecipeCardShowTracker.prototype.setupScrollListener = function () {
1746
+ var _this = this;
1747
+ var scrollTimeout;
1748
+ this.scrollListener = function () {
1749
+ _this.hasScrolled = true;
1750
+ clearTimeout(scrollTimeout);
1751
+ scrollTimeout = window.setTimeout(function () {
1752
+ _this.hasScrolled = false;
1753
+ }, 2000);
1754
+ };
1755
+ document.addEventListener('scroll', this.scrollListener, {
1756
+ passive: true,
1757
+ capture: true,
1758
+ });
1759
+ };
1760
+ RecipeCardShowTracker.prototype.startAutoCleanup = function () {
1761
+ var _this = this;
1762
+ this.cleanupInterval = window.setInterval(function () {
1763
+ _this.cleanOldRecipes();
1764
+ }, this.CLEANUP_INTERVAL);
1765
+ };
1766
+ RecipeCardShowTracker.prototype.setupPageUnloadListener = function () {
1767
+ window.addEventListener('beforeunload', this.handlePageUnload);
1768
+ };
1769
+ return RecipeCardShowTracker;
1770
+ }());
1771
+ RecipeCardShowTracker.instance = null;
1772
+
1773
+ var ViewportListenerParams = /** @class */ (function () {
1774
+ function ViewportListenerParams() {
1775
+ this.condition = true;
1776
+ this.threshold = 0;
1777
+ this.debounce = 0;
1778
+ }
1779
+ return ViewportListenerParams;
1780
+ }());
1781
+ var ViewportListener = /** @class */ (function () {
1782
+ function ViewportListener(element, callback, params) {
1783
+ var _this = this;
1784
+ if (params === void 0) { params = {}; }
1785
+ this.element = element;
1786
+ this.callback = callback;
1787
+ this.params = params;
1788
+ this.intersectionSubject = new rxjs.Subject();
1789
+ this.subscriptions = [];
1790
+ this.params = Object.assign(Object.assign({}, new ViewportListenerParams()), params);
1791
+ var options = {
1792
+ root: null,
1793
+ rootMargin: '0px',
1794
+ threshold: this.params.threshold
1795
+ };
1796
+ this.observer = new IntersectionObserver(function (entries) {
1797
+ entries.forEach(function (entry) { return _this.intersectionSubject.next(entry); });
1798
+ }, options);
1799
+ this.connect();
1800
+ }
1801
+ ViewportListener.prototype.connect = function () {
1802
+ var _this = this;
1803
+ this.subscriptions.push(this.intersectionSubject
1804
+ .pipe(operators.debounceTime(this.params.debounce))
1805
+ .subscribe(function (entry) { return _this.handleIntersection(entry); }));
1806
+ this.observer.observe(this.element);
1807
+ };
1808
+ ViewportListener.prototype.disconnect = function () {
1809
+ if (this.observer) {
1810
+ this.observer.disconnect();
1811
+ }
1812
+ this.intersectionSubject.complete();
1813
+ this.subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
1814
+ };
1815
+ ViewportListener.prototype.handleIntersection = function (entry) {
1816
+ if (this.params.condition && entry.isIntersecting) {
1817
+ this.callback();
1818
+ }
1819
+ };
1820
+ return ViewportListener;
1821
+ }());
1822
+
1823
+ var RECIPE_CARD_SHOW_ANALYTICS_PATH = '/recipes';
1824
+ /** Same visibility rule as `mealz-recipe-card`: IntersectionObserver ratio ≥ this value. */
1825
+ var RECIPE_CARD_SHOW_VISIBILITY_THRESHOLD = 0.8;
1826
+ /** Same timing as `mealz-recipe-card`: debounce on intersection stream (ms). */
1827
+ var RECIPE_CARD_SHOW_DEBOUNCE_MS = 1000;
1828
+ /**
1829
+ * Same `recipe.show` pipeline as `mealz-recipe-card`: viewport visibility + deduped tracking.
1830
+ * Call `disconnect()` when the host node is removed (virtual lists, SPA navigation).
1831
+ */
1832
+ function attachRecipeCardShowTracking(options) {
1833
+ var element = options.element, recipeId = options.recipeId, _a = options.analyticsPath, analyticsPath = _a === void 0 ? RECIPE_CARD_SHOW_ANALYTICS_PATH : _a, _b = options.categoryId, categoryId = _b === void 0 ? '' : _b;
1834
+ var listener = new ViewportListener(element, function () {
1835
+ RecipeCardShowTracker.getInstance().trackRecipeShow(recipeId, analyticsPath, categoryId);
1836
+ }, {
1837
+ threshold: RECIPE_CARD_SHOW_VISIBILITY_THRESHOLD,
1838
+ debounce: RECIPE_CARD_SHOW_DEBOUNCE_MS
1839
+ });
1840
+ return {
1841
+ disconnect: function () { return listener.disconnect(); }
1842
+ };
1843
+ }
1844
+
1649
1845
  var Ingredient = /** @class */ (function (_super) {
1650
1846
  __extends(Ingredient, _super);
1651
1847
  function Ingredient() {
@@ -3452,7 +3648,7 @@
3452
3648
  EventJourney["EMPTY"] = "";
3453
3649
  })(EventJourney || (EventJourney = {}));
3454
3650
 
3455
- var VERSION = "10.5.11"; // TODO: replace by ##VERSION## and update it in the CI/CD
3651
+ var VERSION = "10.6.0"; // TODO: replace by ##VERSION## and update it in the CI/CD
3456
3652
 
3457
3653
  var ContextRegistryService = /** @class */ (function () {
3458
3654
  function ContextRegistryService() {
@@ -11184,7 +11380,8 @@
11184
11380
  _this.analyticsService.init(domain);
11185
11381
  },
11186
11382
  eventSent$: this.analyticsService.eventEmitter,
11187
- setAbTestKey: function (key) { return _this.analyticsService.setAbTestKey(key); }
11383
+ setAbTestKey: function (key) { return _this.analyticsService.setAbTestKey(key); },
11384
+ attachRecipeCardShowTracking: attachRecipeCardShowTracking
11188
11385
  },
11189
11386
  basket: {
11190
11387
  basketIsReady$: this.basketsService.basketStats$.pipe(operators.skipWhile(function (stats) { return !stats; }), operators.take(1), operators.map(function () { return true; })),