Chris@0
|
1 /**
|
Chris@0
|
2 * @file
|
Chris@0
|
3 * JavaScript API for the History module, with client-side caching.
|
Chris@0
|
4 *
|
Chris@0
|
5 * May only be loaded for authenticated users, with the History module enabled.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@17
|
8 (function($, Drupal, drupalSettings, storage) {
|
Chris@0
|
9 const currentUserID = parseInt(drupalSettings.user.uid, 10);
|
Chris@0
|
10
|
Chris@0
|
11 // Any comment that is older than 30 days is automatically considered read,
|
Chris@0
|
12 // so for these we don't need to perform a request at all!
|
Chris@14
|
13 const secondsIn30Days = 2592000;
|
Chris@17
|
14 const thirtyDaysAgo =
|
Chris@17
|
15 Math.round(new Date().getTime() / 1000) - secondsIn30Days;
|
Chris@0
|
16
|
Chris@0
|
17 // Use the data embedded in the page, if available.
|
Chris@0
|
18 let embeddedLastReadTimestamps = false;
|
Chris@0
|
19 if (drupalSettings.history && drupalSettings.history.lastReadTimestamps) {
|
Chris@0
|
20 embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps;
|
Chris@0
|
21 }
|
Chris@0
|
22
|
Chris@0
|
23 /**
|
Chris@0
|
24 * @namespace
|
Chris@0
|
25 */
|
Chris@0
|
26 Drupal.history = {
|
Chris@0
|
27 /**
|
Chris@0
|
28 * Fetch "last read" timestamps for the given nodes.
|
Chris@0
|
29 *
|
Chris@0
|
30 * @param {Array} nodeIDs
|
Chris@0
|
31 * An array of node IDs.
|
Chris@0
|
32 * @param {function} callback
|
Chris@0
|
33 * A callback that is called after the requested timestamps were fetched.
|
Chris@0
|
34 */
|
Chris@0
|
35 fetchTimestamps(nodeIDs, callback) {
|
Chris@0
|
36 // Use the data embedded in the page, if available.
|
Chris@0
|
37 if (embeddedLastReadTimestamps) {
|
Chris@0
|
38 callback();
|
Chris@0
|
39 return;
|
Chris@0
|
40 }
|
Chris@0
|
41
|
Chris@0
|
42 $.ajax({
|
Chris@0
|
43 url: Drupal.url('history/get_node_read_timestamps'),
|
Chris@0
|
44 type: 'POST',
|
Chris@0
|
45 data: { 'node_ids[]': nodeIDs },
|
Chris@0
|
46 dataType: 'json',
|
Chris@0
|
47 success(results) {
|
Chris@17
|
48 Object.keys(results || {}).forEach(nodeID => {
|
Chris@17
|
49 storage.setItem(
|
Chris@17
|
50 `Drupal.history.${currentUserID}.${nodeID}`,
|
Chris@17
|
51 results[nodeID],
|
Chris@17
|
52 );
|
Chris@14
|
53 });
|
Chris@0
|
54 callback();
|
Chris@0
|
55 },
|
Chris@0
|
56 });
|
Chris@0
|
57 },
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Get the last read timestamp for the given node.
|
Chris@0
|
61 *
|
Chris@0
|
62 * @param {number|string} nodeID
|
Chris@0
|
63 * A node ID.
|
Chris@0
|
64 *
|
Chris@0
|
65 * @return {number}
|
Chris@0
|
66 * A UNIX timestamp.
|
Chris@0
|
67 */
|
Chris@0
|
68 getLastRead(nodeID) {
|
Chris@0
|
69 // Use the data embedded in the page, if available.
|
Chris@0
|
70 if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
|
Chris@0
|
71 return parseInt(embeddedLastReadTimestamps[nodeID], 10);
|
Chris@0
|
72 }
|
Chris@17
|
73 return parseInt(
|
Chris@17
|
74 storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0,
|
Chris@17
|
75 10,
|
Chris@17
|
76 );
|
Chris@0
|
77 },
|
Chris@0
|
78
|
Chris@0
|
79 /**
|
Chris@0
|
80 * Marks a node as read, store the last read timestamp client-side.
|
Chris@0
|
81 *
|
Chris@0
|
82 * @param {number|string} nodeID
|
Chris@0
|
83 * A node ID.
|
Chris@0
|
84 */
|
Chris@0
|
85 markAsRead(nodeID) {
|
Chris@0
|
86 $.ajax({
|
Chris@0
|
87 url: Drupal.url(`history/${nodeID}/read`),
|
Chris@0
|
88 type: 'POST',
|
Chris@0
|
89 dataType: 'json',
|
Chris@0
|
90 success(timestamp) {
|
Chris@0
|
91 // If the data is embedded in the page, don't store on the client
|
Chris@0
|
92 // side.
|
Chris@17
|
93 if (
|
Chris@17
|
94 embeddedLastReadTimestamps &&
|
Chris@17
|
95 embeddedLastReadTimestamps[nodeID]
|
Chris@17
|
96 ) {
|
Chris@0
|
97 return;
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@17
|
100 storage.setItem(
|
Chris@17
|
101 `Drupal.history.${currentUserID}.${nodeID}`,
|
Chris@17
|
102 timestamp,
|
Chris@17
|
103 );
|
Chris@0
|
104 },
|
Chris@0
|
105 });
|
Chris@0
|
106 },
|
Chris@0
|
107
|
Chris@0
|
108 /**
|
Chris@0
|
109 * Determines whether a server check is necessary.
|
Chris@0
|
110 *
|
Chris@0
|
111 * Any content that is >30 days old never gets a "new" or "updated"
|
Chris@0
|
112 * indicator. Any content that was published before the oldest known reading
|
Chris@0
|
113 * also never gets a "new" or "updated" indicator, because it must've been
|
Chris@0
|
114 * read already.
|
Chris@0
|
115 *
|
Chris@0
|
116 * @param {number|string} nodeID
|
Chris@0
|
117 * A node ID.
|
Chris@0
|
118 * @param {number} contentTimestamp
|
Chris@0
|
119 * The time at which some content (e.g. a comment) was published.
|
Chris@0
|
120 *
|
Chris@0
|
121 * @return {bool}
|
Chris@0
|
122 * Whether a server check is necessary for the given node and its
|
Chris@0
|
123 * timestamp.
|
Chris@0
|
124 */
|
Chris@0
|
125 needsServerCheck(nodeID, contentTimestamp) {
|
Chris@0
|
126 // First check if the content is older than 30 days, then we can bail
|
Chris@0
|
127 // early.
|
Chris@0
|
128 if (contentTimestamp < thirtyDaysAgo) {
|
Chris@0
|
129 return false;
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 // Use the data embedded in the page, if available.
|
Chris@0
|
133 if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
|
Chris@17
|
134 return (
|
Chris@17
|
135 contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10)
|
Chris@17
|
136 );
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@17
|
139 const minLastReadTimestamp = parseInt(
|
Chris@17
|
140 storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0,
|
Chris@17
|
141 10,
|
Chris@17
|
142 );
|
Chris@0
|
143 return contentTimestamp > minLastReadTimestamp;
|
Chris@0
|
144 },
|
Chris@0
|
145 };
|
Chris@17
|
146 })(jQuery, Drupal, drupalSettings, window.localStorage);
|