odoo.define('website_sale.recently_viewed', function (require) { var concurrency = require('web.concurrency'); var config = require('web.config'); var core = require('web.core'); var publicWidget = require('web.public.widget'); var utils = require('web.utils'); var wSaleUtils = require('website_sale.utils'); var qweb = core.qweb; publicWidget.registry.productsRecentlyViewedSnippet = publicWidget.Widget.extend({ selector: '.s_wsale_products_recently_viewed', xmlDependencies: ['/website_sale/static/src/xml/website_sale_recently_viewed.xml'], disabledInEditableMode: false, read_events: { 'click .js_add_cart': '_onAddToCart', 'click .js_remove': '_onRemove', }, /** * @constructor */ init: function () { this._super.apply(this, arguments); this._dp = new concurrency.DropPrevious(); this.uniqueId = _.uniqueId('o_carousel_recently_viewed_products_'); this._onResizeChange = _.debounce(this._addCarousel, 100); }, /** * @override */ start: function () { this._dp.add(this._fetch()).then(this._render.bind(this)); $(window).resize(() => { this._onResizeChange(); }); return this._super.apply(this, arguments); }, /** * @override */ destroy: function () { this._super(...arguments); this.$el.addClass('d-none'); this.$el.find('.slider').html(''); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * @private */ _fetch: function () { return this._rpc({ route: '/shop/products/recently_viewed', }).then(res => { var products = res['products']; // In edit mode, if the current visitor has no recently viewed // products, use demo data. if (this.editableMode && (!products || !products.length)) { return { 'products': [{ id: 0, website_url: '#', display_name: 'Product 1', price: '$ 750.00', }, { id: 0, website_url: '#', display_name: 'Product 2', price: '$ 750.00', }, { id: 0, website_url: '#', display_name: 'Product 3', price: '$ 750.00', }, { id: 0, website_url: '#', display_name: 'Product 4', price: '$ 750.00', }], }; } return res; }); }, /** * @private */ _render: function (res) { var products = res['products']; var mobileProducts = [], webProducts = [], productsTemp = []; _.each(products, function (product) { if (productsTemp.length === 4) { webProducts.push(productsTemp); productsTemp = []; } productsTemp.push(product); mobileProducts.push([product]); }); if (productsTemp.length) { webProducts.push(productsTemp); } this.mobileCarousel = $(qweb.render('website_sale.productsRecentlyViewed', { uniqueId: this.uniqueId, productFrame: 1, productsGroups: mobileProducts, })); this.webCarousel = $(qweb.render('website_sale.productsRecentlyViewed', { uniqueId: this.uniqueId, productFrame: 4, productsGroups: webProducts, })); this._addCarousel(); this.$el.toggleClass('d-none', !(products && products.length)); }, /** * Add the right carousel depending on screen size. * @private */ _addCarousel: function () { var carousel = config.device.size_class <= config.device.SIZES.SM ? this.mobileCarousel : this.webCarousel; this.$('.slider').html(carousel).css('display', ''); // Removing display is kept for compatibility (it was hidden before) }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * Add product to cart and reload the carousel. * @private * @param {Event} ev */ _onAddToCart: function (ev) { var self = this; var $card = $(ev.currentTarget).closest('.card'); this._rpc({ route: "/shop/cart/update_json", params: { product_id: $card.find('input[data-product-id]').data('product-id'), add_qty: 1 }, }).then(function (data) { wSaleUtils.updateCartNavBar(data); var $navButton = $('header .o_wsale_my_cart').first(); var fetch = self._fetch(); var animation = wSaleUtils.animateClone($navButton, $(ev.currentTarget).parents('.o_carousel_product_card'), 25, 40); Promise.all([fetch, animation]).then(function (values) { self._render(values[0]); }); }); }, /** * Remove product from recently viewed products. * @private * @param {Event} ev */ _onRemove: function (ev) { var self = this; var $card = $(ev.currentTarget).closest('.card'); this._rpc({ route: "/shop/products/recently_viewed_delete", params: { product_id: $card.find('input[data-product-id]').data('product-id'), }, }).then(function (data) { self._render(data); }); }, }); publicWidget.registry.productsRecentlyViewedUpdate = publicWidget.Widget.extend({ selector: '#product_detail', events: { 'change input.product_id[name="product_id"]': '_onProductChange', }, debounceValue: 8000, /** * @constructor */ init: function () { this._super.apply(this, arguments); this._onProductChange = _.debounce(this._onProductChange, this.debounceValue); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * Debounced method that wait some time before marking the product as viewed. * @private * @param {HTMLInputElement} $input */ _updateProductView: function ($input) { var productId = parseInt($input.val()); var cookieName = 'seen_product_id_' + productId; if (! parseInt(this.el.dataset.viewTrack, 10)) { return; // Is not tracked } if (utils.get_cookie(cookieName)) { return; // Already tracked in the last 30min } if ($(this.el).find('.js_product.css_not_available').length) { return; // Variant not possible } this._rpc({ route: '/shop/products/recently_viewed_update', params: { product_id: productId, } }).then(function (res) { if (res && res.visitor_uuid) { utils.set_cookie('visitor_uuid', res.visitor_uuid); } utils.set_cookie(cookieName, productId, 30 * 60); }); }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * Call debounced method when product change to reset timer. * @private * @param {Event} ev */ _onProductChange: function (ev) { this._updateProductView($(ev.currentTarget)); }, }); });