Chris@0: /** Chris@0: * @file Chris@0: * JavaScript API for the History module, with client-side caching. Chris@0: * Chris@0: * May only be loaded for authenticated users, with the History module enabled. Chris@0: */ Chris@0: Chris@0: (function ($, Drupal, drupalSettings, storage) { Chris@0: const currentUserID = parseInt(drupalSettings.user.uid, 10); Chris@0: Chris@0: // Any comment that is older than 30 days is automatically considered read, Chris@0: // so for these we don't need to perform a request at all! Chris@0: const secondsIn30Days = 2592000; Chris@0: const thirtyDaysAgo = Math.round(new Date().getTime() / 1000) - secondsIn30Days; Chris@0: Chris@0: // Use the data embedded in the page, if available. Chris@0: let embeddedLastReadTimestamps = false; Chris@0: if (drupalSettings.history && drupalSettings.history.lastReadTimestamps) { Chris@0: embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @namespace Chris@0: */ Chris@0: Drupal.history = { Chris@0: Chris@0: /** Chris@0: * Fetch "last read" timestamps for the given nodes. Chris@0: * Chris@0: * @param {Array} nodeIDs Chris@0: * An array of node IDs. Chris@0: * @param {function} callback Chris@0: * A callback that is called after the requested timestamps were fetched. Chris@0: */ Chris@0: fetchTimestamps(nodeIDs, callback) { Chris@0: // Use the data embedded in the page, if available. Chris@0: if (embeddedLastReadTimestamps) { Chris@0: callback(); Chris@0: return; Chris@0: } Chris@0: Chris@0: $.ajax({ Chris@0: url: Drupal.url('history/get_node_read_timestamps'), Chris@0: type: 'POST', Chris@0: data: { 'node_ids[]': nodeIDs }, Chris@0: dataType: 'json', Chris@0: success(results) { Chris@0: Object.keys(results || {}).forEach((nodeID) => { Chris@0: storage.setItem(`Drupal.history.${currentUserID}.${nodeID}`, results[nodeID]); Chris@0: }); Chris@0: callback(); Chris@0: }, Chris@0: }); Chris@0: }, Chris@0: Chris@0: /** Chris@0: * Get the last read timestamp for the given node. Chris@0: * Chris@0: * @param {number|string} nodeID Chris@0: * A node ID. Chris@0: * Chris@0: * @return {number} Chris@0: * A UNIX timestamp. Chris@0: */ Chris@0: getLastRead(nodeID) { Chris@0: // Use the data embedded in the page, if available. Chris@0: if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) { Chris@0: return parseInt(embeddedLastReadTimestamps[nodeID], 10); Chris@0: } Chris@0: return parseInt(storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0, 10); Chris@0: }, Chris@0: Chris@0: /** Chris@0: * Marks a node as read, store the last read timestamp client-side. Chris@0: * Chris@0: * @param {number|string} nodeID Chris@0: * A node ID. Chris@0: */ Chris@0: markAsRead(nodeID) { Chris@0: $.ajax({ Chris@0: url: Drupal.url(`history/${nodeID}/read`), Chris@0: type: 'POST', Chris@0: dataType: 'json', Chris@0: success(timestamp) { Chris@0: // If the data is embedded in the page, don't store on the client Chris@0: // side. Chris@0: if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) { Chris@0: return; Chris@0: } Chris@0: Chris@0: storage.setItem(`Drupal.history.${currentUserID}.${nodeID}`, timestamp); Chris@0: }, Chris@0: }); Chris@0: }, Chris@0: Chris@0: /** Chris@0: * Determines whether a server check is necessary. Chris@0: * Chris@0: * Any content that is >30 days old never gets a "new" or "updated" Chris@0: * indicator. Any content that was published before the oldest known reading Chris@0: * also never gets a "new" or "updated" indicator, because it must've been Chris@0: * read already. Chris@0: * Chris@0: * @param {number|string} nodeID Chris@0: * A node ID. Chris@0: * @param {number} contentTimestamp Chris@0: * The time at which some content (e.g. a comment) was published. Chris@0: * Chris@0: * @return {bool} Chris@0: * Whether a server check is necessary for the given node and its Chris@0: * timestamp. Chris@0: */ Chris@0: needsServerCheck(nodeID, contentTimestamp) { Chris@0: // First check if the content is older than 30 days, then we can bail Chris@0: // early. Chris@0: if (contentTimestamp < thirtyDaysAgo) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: // Use the data embedded in the page, if available. Chris@0: if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) { Chris@0: return contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10); Chris@0: } Chris@0: Chris@0: const minLastReadTimestamp = parseInt(storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0, 10); Chris@0: return contentTimestamp > minLastReadTimestamp; Chris@0: }, Chris@0: }; Chris@0: }(jQuery, Drupal, drupalSettings, window.localStorage));