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@17: (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@14: const secondsIn30Days = 2592000; Chris@17: const thirtyDaysAgo = Chris@17: 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: * 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@17: Object.keys(results || {}).forEach(nodeID => { Chris@17: storage.setItem( Chris@17: `Drupal.history.${currentUserID}.${nodeID}`, Chris@17: results[nodeID], Chris@17: ); Chris@14: }); 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@17: return parseInt( Chris@17: storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0, Chris@17: 10, Chris@17: ); 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@17: if ( Chris@17: embeddedLastReadTimestamps && Chris@17: embeddedLastReadTimestamps[nodeID] Chris@17: ) { Chris@0: return; Chris@0: } Chris@0: Chris@17: storage.setItem( Chris@17: `Drupal.history.${currentUserID}.${nodeID}`, Chris@17: timestamp, Chris@17: ); 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@17: return ( Chris@17: contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10) Chris@17: ); Chris@0: } Chris@0: Chris@17: const minLastReadTimestamp = parseInt( Chris@17: storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0, Chris@17: 10, Chris@17: ); Chris@0: return contentTimestamp > minLastReadTimestamp; Chris@0: }, Chris@0: }; Chris@17: })(jQuery, Drupal, drupalSettings, window.localStorage);