customer-data.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /**
  6. * @api
  7. */
  8. define([
  9. 'jquery',
  10. 'underscore',
  11. 'ko',
  12. 'Magento_Customer/js/section-config',
  13. 'mage/storage',
  14. 'jquery/jquery-storageapi'
  15. ], function ($, _, ko, sectionConfig) {
  16. 'use strict';
  17. var options,
  18. storage,
  19. storageInvalidation,
  20. invalidateCacheBySessionTimeOut,
  21. invalidateCacheByCloseCookieSession,
  22. dataProvider,
  23. buffer,
  24. customerData;
  25. //TODO: remove global change, in this case made for initNamespaceStorage
  26. $.cookieStorage.setConf({
  27. path: '/',
  28. expires: 1
  29. });
  30. storage = $.initNamespaceStorage('mage-cache-storage').localStorage;
  31. storageInvalidation = $.initNamespaceStorage('mage-cache-storage-section-invalidation').localStorage;
  32. /**
  33. * @param {Object} invalidateOptions
  34. */
  35. invalidateCacheBySessionTimeOut = function (invalidateOptions) {
  36. var date;
  37. if (new Date($.localStorage.get('mage-cache-timeout')) < new Date()) {
  38. storage.removeAll();
  39. date = new Date(Date.now() + parseInt(invalidateOptions.cookieLifeTime, 10) * 1000);
  40. $.localStorage.set('mage-cache-timeout', date);
  41. }
  42. };
  43. /**
  44. * Invalidate Cache By Close Cookie Session
  45. */
  46. invalidateCacheByCloseCookieSession = function () {
  47. if (!$.cookieStorage.isSet('mage-cache-sessid')) {
  48. $.cookieStorage.set('mage-cache-sessid', true);
  49. storage.removeAll();
  50. }
  51. };
  52. dataProvider = {
  53. /**
  54. * @param {Object} sectionNames
  55. * @return {Object}
  56. */
  57. getFromStorage: function (sectionNames) {
  58. var result = {};
  59. _.each(sectionNames, function (sectionName) {
  60. result[sectionName] = storage.get(sectionName);
  61. });
  62. return result;
  63. },
  64. /**
  65. * @param {Object} sectionNames
  66. * @param {Boolean} forceNewSectionTimestamp
  67. * @return {*}
  68. */
  69. getFromServer: function (sectionNames, forceNewSectionTimestamp) {
  70. var parameters;
  71. sectionNames = sectionConfig.filterClientSideSections(sectionNames);
  72. parameters = _.isArray(sectionNames) ? {
  73. sections: sectionNames.join(',')
  74. } : [];
  75. parameters['force_new_section_timestamp'] = forceNewSectionTimestamp;
  76. return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) {
  77. throw new Error(jqXHR);
  78. });
  79. }
  80. };
  81. /**
  82. * @param {Function} target
  83. * @param {String} sectionName
  84. * @return {*}
  85. */
  86. ko.extenders.disposableCustomerData = function (target, sectionName) {
  87. var sectionDataIds, newSectionDataIds = {};
  88. target.subscribe(function () {
  89. setTimeout(function () {
  90. storage.remove(sectionName);
  91. sectionDataIds = $.cookieStorage.get('section_data_ids') || {};
  92. _.each(sectionDataIds, function (data, name) {
  93. if (name != sectionName) { //eslint-disable-line eqeqeq
  94. newSectionDataIds[name] = data;
  95. }
  96. });
  97. $.cookieStorage.set('section_data_ids', newSectionDataIds);
  98. }, 3000);
  99. });
  100. return target;
  101. };
  102. buffer = {
  103. data: {},
  104. /**
  105. * @param {String} sectionName
  106. */
  107. bind: function (sectionName) {
  108. this.data[sectionName] = ko.observable({});
  109. },
  110. /**
  111. * @param {String} sectionName
  112. * @return {Object}
  113. */
  114. get: function (sectionName) {
  115. if (!this.data[sectionName]) {
  116. this.bind(sectionName);
  117. }
  118. return this.data[sectionName];
  119. },
  120. /**
  121. * @return {Array}
  122. */
  123. keys: function () {
  124. return _.keys(this.data);
  125. },
  126. /**
  127. * @param {String} sectionName
  128. * @param {Object} sectionData
  129. */
  130. notify: function (sectionName, sectionData) {
  131. if (!this.data[sectionName]) {
  132. this.bind(sectionName);
  133. }
  134. this.data[sectionName](sectionData);
  135. },
  136. /**
  137. * @param {Object} sections
  138. */
  139. update: function (sections) {
  140. var sectionId = 0,
  141. sectionDataIds = $.cookieStorage.get('section_data_ids') || {};
  142. _.each(sections, function (sectionData, sectionName) {
  143. sectionId = sectionData['data_id'];
  144. sectionDataIds[sectionName] = sectionId;
  145. storage.set(sectionName, sectionData);
  146. storageInvalidation.remove(sectionName);
  147. buffer.notify(sectionName, sectionData);
  148. });
  149. $.cookieStorage.set('section_data_ids', sectionDataIds);
  150. },
  151. /**
  152. * @param {Object} sections
  153. */
  154. remove: function (sections) {
  155. _.each(sections, function (sectionName) {
  156. storage.remove(sectionName);
  157. if (!sectionConfig.isClientSideSection(sectionName)) {
  158. storageInvalidation.set(sectionName, true);
  159. }
  160. });
  161. }
  162. };
  163. customerData = {
  164. /**
  165. * Customer data initialization
  166. */
  167. init: function () {
  168. var countryData,
  169. privateContentVersion = 'private_content_version',
  170. privateContent = $.cookieStorage.get(privateContentVersion),
  171. localPrivateContent = $.localStorage.get(privateContentVersion),
  172. needVersion = 'need_version',
  173. expiredSectionNames = this.getExpiredSectionNames(),
  174. isLoading = false;
  175. if (privateContent &&
  176. !$.cookieStorage.isSet(privateContentVersion) &&
  177. !$.localStorage.isSet(privateContentVersion)
  178. ) {
  179. $.cookieStorage.set(privateContentVersion, needVersion);
  180. $.localStorage.set(privateContentVersion, needVersion);
  181. this.reload([], false);
  182. isLoading = true;
  183. } else if (localPrivateContent !== privateContent) {
  184. if (!$.cookieStorage.isSet(privateContentVersion)) {
  185. privateContent = needVersion;
  186. $.cookieStorage.set(privateContentVersion, privateContent);
  187. }
  188. $.localStorage.set(privateContentVersion, privateContent);
  189. _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
  190. buffer.notify(sectionName, sectionData);
  191. });
  192. this.reload([], false);
  193. isLoading = true;
  194. } else if (expiredSectionNames.length > 0) {
  195. _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
  196. buffer.notify(sectionName, sectionData);
  197. });
  198. this.reload(expiredSectionNames, false);
  199. } else {
  200. _.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
  201. buffer.notify(sectionName, sectionData);
  202. });
  203. if (!_.isEmpty(storageInvalidation.keys())) {
  204. this.reload(storageInvalidation.keys(), false);
  205. }
  206. }
  207. if (!_.isEmpty(privateContent)) {
  208. countryData = this.get('directory-data');
  209. if (_.isEmpty(countryData()) && !isLoading) {
  210. customerData.reload(['directory-data'], false);
  211. }
  212. }
  213. },
  214. /**
  215. * Retrieve the list of sections that has expired since last page reload.
  216. *
  217. * Sections can expire due to lifetime constraints or due to inconsistent storage information
  218. * (validated by cookie data).
  219. *
  220. * @return {Array}
  221. */
  222. getExpiredSectionNames: function () {
  223. var expiredSectionNames = [],
  224. cookieSectionTimestamps = $.cookieStorage.get('section_data_ids') || {},
  225. sectionLifetime = options.expirableSectionLifetime * 60,
  226. currentTimestamp = Math.floor(Date.now() / 1000),
  227. sectionData;
  228. // process sections that can expire due to lifetime constraints
  229. _.each(options.expirableSectionNames, function (sectionName) {
  230. sectionData = storage.get(sectionName);
  231. if (typeof sectionData === 'object' && sectionData['data_id'] + sectionLifetime <= currentTimestamp) {
  232. expiredSectionNames.push(sectionName);
  233. }
  234. });
  235. // process sections that can expire due to storage information inconsistency
  236. _.each(cookieSectionTimestamps, function (cookieSectionTimestamp, sectionName) {
  237. sectionData = storage.get(sectionName);
  238. if (typeof sectionData === 'undefined' ||
  239. typeof sectionData === 'object' &&
  240. cookieSectionTimestamp != sectionData['data_id'] //eslint-disable-line
  241. ) {
  242. expiredSectionNames.push(sectionName);
  243. }
  244. });
  245. return _.uniq(expiredSectionNames);
  246. },
  247. /**
  248. * Check if some sections have to be reloaded.
  249. *
  250. * @deprecated Use getExpiredSectionNames instead.
  251. *
  252. * @return {Boolean}
  253. */
  254. needReload: function () {
  255. var expiredSectionNames = this.getExpiredSectionNames();
  256. return expiredSectionNames.length > 0;
  257. },
  258. /**
  259. * Retrieve the list of expired keys.
  260. *
  261. * @deprecated Use getExpiredSectionNames instead.
  262. *
  263. * @return {Array}
  264. */
  265. getExpiredKeys: function () {
  266. return this.getExpiredSectionNames();
  267. },
  268. /**
  269. * @param {String} sectionName
  270. * @return {*}
  271. */
  272. get: function (sectionName) {
  273. return buffer.get(sectionName);
  274. },
  275. /**
  276. * @param {String} sectionName
  277. * @param {Object} sectionData
  278. */
  279. set: function (sectionName, sectionData) {
  280. var data = {};
  281. data[sectionName] = sectionData;
  282. buffer.update(data);
  283. },
  284. /**
  285. * @param {Array} sectionNames
  286. * @param {Boolean} forceNewSectionTimestamp
  287. * @return {*}
  288. */
  289. reload: function (sectionNames, forceNewSectionTimestamp) {
  290. return dataProvider.getFromServer(sectionNames, forceNewSectionTimestamp).done(function (sections) {
  291. $(document).trigger('customer-data-reload', [sectionNames]);
  292. buffer.update(sections);
  293. });
  294. },
  295. /**
  296. * @param {Array} sectionNames
  297. */
  298. invalidate: function (sectionNames) {
  299. var sectionDataIds,
  300. sectionsNamesForInvalidation;
  301. sectionsNamesForInvalidation = _.contains(sectionNames, '*') ? buffer.keys() : sectionNames;
  302. $(document).trigger('customer-data-invalidate', [sectionsNamesForInvalidation]);
  303. buffer.remove(sectionsNamesForInvalidation);
  304. sectionDataIds = $.cookieStorage.get('section_data_ids') || {};
  305. // Invalidate section in cookie (increase version of section with 1000)
  306. _.each(sectionsNamesForInvalidation, function (sectionName) {
  307. if (!sectionConfig.isClientSideSection(sectionName)) {
  308. sectionDataIds[sectionName] += 1000;
  309. }
  310. });
  311. $.cookieStorage.set('section_data_ids', sectionDataIds);
  312. },
  313. /**
  314. * @param {Object} settings
  315. * @constructor
  316. */
  317. 'Magento_Customer/js/customer-data': function (settings) {
  318. options = settings;
  319. invalidateCacheBySessionTimeOut(settings);
  320. invalidateCacheByCloseCookieSession();
  321. customerData.init();
  322. }
  323. };
  324. /**
  325. * Events listener
  326. */
  327. $(document).on('ajaxComplete', function (event, xhr, settings) {
  328. var sections,
  329. redirects;
  330. if (settings.type.match(/post|put|delete/i)) {
  331. sections = sectionConfig.getAffectedSections(settings.url);
  332. if (sections) {
  333. customerData.invalidate(sections);
  334. redirects = ['redirect', 'backUrl'];
  335. if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) { //eslint-disable-line
  336. return;
  337. }
  338. customerData.reload(sections, true);
  339. }
  340. }
  341. });
  342. /**
  343. * Events listener
  344. */
  345. $(document).on('submit', function (event) {
  346. var sections;
  347. if (event.target.method.match(/post|put|delete/i)) {
  348. sections = sectionConfig.getAffectedSections(event.target.action);
  349. if (sections) {
  350. customerData.invalidate(sections);
  351. }
  352. }
  353. });
  354. return customerData;
  355. });