##// END OF EJS Templates
Blink metadata only once after thread update
neko259 -
r1126:ef65332b default
parent child Browse files
Show More
@@ -1,339 +1,353 b''
1 1 /*
2 2 @licstart The following is the entire license notice for the
3 3 JavaScript code in this page.
4 4
5 5
6 6 Copyright (C) 2013-2014 neko259
7 7
8 8 The JavaScript code in this page is free software: you can
9 9 redistribute it and/or modify it under the terms of the GNU
10 10 General Public License (GNU GPL) as published by the Free Software
11 11 Foundation, either version 3 of the License, or (at your option)
12 12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15 15
16 16 As additional permission under GNU GPL version 3 section 7, you
17 17 may distribute non-source (e.g., minimized or compacted) forms of
18 18 that code without the copy of the GNU GPL normally required by
19 19 section 4, provided you include this license notice and a URL
20 20 through which recipients can access the Corresponding Source.
21 21
22 22 @licend The above is the entire license notice
23 23 for the JavaScript code in this page.
24 24 */
25 25
26 26 var CLASS_POST = '.post'
27 27
28 var POST_ADDED = 0;
29 var POST_UPDATED = 1;
30
28 31 var wsUser = '';
29 32
30 33 var unreadPosts = 0;
31 34 var documentOriginalTitle = '';
32 35
33 36 // Thread ID does not change, can be stored one time
34 37 var threadId = $('div.thread').children(CLASS_POST).first().attr('id');
35 38
36 39 /**
37 40 * Connect to websocket server and subscribe to thread updates. On any update we
38 41 * request a thread diff.
39 42 *
40 43 * @returns {boolean} true if connected, false otherwise
41 44 */
42 45 function connectWebsocket() {
43 46 var metapanel = $('.metapanel')[0];
44 47
45 48 var wsHost = metapanel.getAttribute('data-ws-host');
46 49 var wsPort = metapanel.getAttribute('data-ws-port');
47 50
48 51 if (wsHost.length > 0 && wsPort.length > 0)
49 52 var centrifuge = new Centrifuge({
50 53 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
51 54 "project": metapanel.getAttribute('data-ws-project'),
52 55 "user": wsUser,
53 56 "timestamp": metapanel.getAttribute('data-ws-token-time'),
54 57 "token": metapanel.getAttribute('data-ws-token'),
55 58 "debug": false
56 59 });
57 60
58 61 centrifuge.on('error', function(error_message) {
59 62 console.log("Error connecting to websocket server.");
60 63 console.log(error_message);
61 64 return false;
62 65 });
63 66
64 67 centrifuge.on('connect', function() {
65 68 var channelName = 'thread:' + threadId;
66 69 centrifuge.subscribe(channelName, function(message) {
67 70 getThreadDiff();
68 71 });
69 72
70 73 // For the case we closed the browser and missed some updates
71 74 getThreadDiff();
72 75 $('#autoupdate').hide();
73 76 });
74 77
75 78 centrifuge.connect();
76 79
77 80 return true;
78 81 }
79 82
80 83 /**
81 84 * Get diff of the posts from the current thread timestamp.
82 85 * This is required if the browser was closed and some post updates were
83 86 * missed.
84 87 */
85 88 function getThreadDiff() {
86 89 var lastUpdateTime = $('.metapanel').attr('data-last-update');
87 90 var lastPostId = $('.post').last().attr('id');
88 91
89 92 var uids = '';
90 93 var posts = $('.post');
91 94 for (var i = 0; i < posts.length; i++) {
92 95 uids += posts[i].getAttribute('data-uid') + ' ';
93 96 }
94 97
95 98 var data = {
96 99 uids: uids
97 100 }
98 101
99 102 var diffUrl = '/api/diff_thread?thread=' + threadId;
100 103
101 104 $.post(diffUrl,
102 105 data,
103 106 function(data) {
104 107 var updatedPosts = data.updated;
108 var addedPostCount = 0;
105 109
106 110 for (var i = 0; i < updatedPosts.length; i++) {
107 111 var postText = updatedPosts[i];
108 112 var post = $(postText);
109 113
110 updatePost(post);
114 if (updatePost(post) == POST_ADDED) {
115 addedPostCount++;
116 }
111 117 }
112 118
113 119 var hasMetaUpdates = updatedPosts.length > 0;
114 120 if (hasMetaUpdates) {
115 121 updateMetadataPanel();
116 122 }
117 123
124 updateBumplimitProgress(addedPostCount);
125 showNewPostsTitle(addedPostCount);
126
118 127 // TODO Process removed posts if any
119 128 $('.metapanel').attr('data-last-update', data.last_update);
120 129 },
121 130 'json'
122 131 )
123 132 }
124 133
125 134 /**
126 135 * Add or update the post on html page.
127 136 */
128 137 function updatePost(postHtml) {
129 138 // This needs to be set on start because the page is scrolled after posts
130 139 // are added or updated
131 140 var bottom = isPageBottom();
132 141
133 142 var post = $(postHtml);
134 143
135 144 var threadBlock = $('div.thread');
136 145
137 146 var postId = post.attr('id');
138 147
139 148 // If the post already exists, replace it. Otherwise add as a new one.
140 149 var existingPosts = threadBlock.children('.post[id=' + postId + ']');
141 150
151 var type;
152
142 153 if (existingPosts.size() > 0) {
143 154 existingPosts.replaceWith(post);
155
156 type = POST_UPDATED;
144 157 } else {
145 158 post.appendTo(threadBlock);
146 159
147 160 if (bottom) {
148 161 scrollToBottom();
149 162 }
150 163
151 updateBumplimitProgress(1);
152 showNewPostsTitle(1);
164 type = POST_ADDED;
153 165 }
154 166
155 167 processNewPost(post);
168
169 return type;
156 170 }
157 171
158 172 /**
159 173 * Initiate a blinking animation on a node to show it was updated.
160 174 */
161 175 function blink(node) {
162 176 var blinkCount = 2;
163 177
164 178 var nodeToAnimate = node;
165 179 for (var i = 0; i < blinkCount; i++) {
166 180 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
167 181 }
168 182 }
169 183
170 184 function isPageBottom() {
171 185 var scroll = $(window).scrollTop() / ($(document).height()
172 186 - $(window).height());
173 187
174 188 return scroll == 1
175 189 }
176 190
177 191 function initAutoupdate() {
178 192 return connectWebsocket();
179 193 }
180 194
181 195 function getReplyCount() {
182 196 return $('.thread').children(CLASS_POST).length
183 197 }
184 198
185 199 function getImageCount() {
186 200 return $('.thread').find('img').length
187 201 }
188 202
189 203 /**
190 204 * Update post count, images count and last update time in the metadata
191 205 * panel.
192 206 */
193 207 function updateMetadataPanel() {
194 208 var replyCountField = $('#reply-count');
195 209 var imageCountField = $('#image-count');
196 210
197 211 replyCountField.text(getReplyCount());
198 212 imageCountField.text(getImageCount());
199 213
200 214 var lastUpdate = $('.post:last').children('.post-info').first()
201 215 .children('.pub_time').first().html();
202 216 if (lastUpdate !== '') {
203 217 var lastUpdateField = $('#last-update');
204 218 lastUpdateField.html(lastUpdate);
205 219 blink(lastUpdateField);
206 220 }
207 221
208 222 blink(replyCountField);
209 223 blink(imageCountField);
210 224 }
211 225
212 226 /**
213 227 * Update bumplimit progress bar
214 228 */
215 229 function updateBumplimitProgress(postDelta) {
216 230 var progressBar = $('#bumplimit_progress');
217 231 if (progressBar) {
218 232 var postsToLimitElement = $('#left_to_limit');
219 233
220 234 var oldPostsToLimit = parseInt(postsToLimitElement.text());
221 235 var postCount = getReplyCount();
222 236 var bumplimit = postCount - postDelta + oldPostsToLimit;
223 237
224 238 var newPostsToLimit = bumplimit - postCount;
225 239 if (newPostsToLimit <= 0) {
226 240 $('.bar-bg').remove();
227 241 } else {
228 242 postsToLimitElement.text(newPostsToLimit);
229 243 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
230 244 }
231 245 }
232 246 }
233 247
234 248 /**
235 249 * Show 'new posts' text in the title if the document is not visible to a user
236 250 */
237 251 function showNewPostsTitle(newPostCount) {
238 252 if (document.hidden) {
239 253 if (documentOriginalTitle === '') {
240 254 documentOriginalTitle = document.title;
241 255 }
242 256 unreadPosts = unreadPosts + newPostCount;
243 257 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
244 258
245 259 document.addEventListener('visibilitychange', function() {
246 260 if (documentOriginalTitle !== '') {
247 261 document.title = documentOriginalTitle;
248 262 documentOriginalTitle = '';
249 263 unreadPosts = 0;
250 264 }
251 265
252 266 document.removeEventListener('visibilitychange', null);
253 267 });
254 268 }
255 269 }
256 270
257 271 /**
258 272 * Clear all entered values in the form fields
259 273 */
260 274 function resetForm(form) {
261 275 form.find('input:text, input:password, input:file, select, textarea').val('');
262 276 form.find('input:radio, input:checkbox')
263 277 .removeAttr('checked').removeAttr('selected');
264 278 $('.file_wrap').find('.file-thumb').remove();
265 279 }
266 280
267 281 /**
268 282 * When the form is posted, this method will be run as a callback
269 283 */
270 284 function updateOnPost(response, statusText, xhr, form) {
271 285 var json = $.parseJSON(response);
272 286 var status = json.status;
273 287
274 288 showAsErrors(form, '');
275 289
276 290 if (status === 'ok') {
277 291 resetFormPosition();
278 292 resetForm(form);
279 293 getThreadDiff();
280 294 scrollToBottom();
281 295 } else {
282 296 var errors = json.errors;
283 297 for (var i = 0; i < errors.length; i++) {
284 298 var fieldErrors = errors[i];
285 299
286 300 var error = fieldErrors.errors;
287 301
288 302 showAsErrors(form, error);
289 303 }
290 304 }
291 305 }
292 306
293 307 /**
294 308 * Show text in the errors row of the form.
295 309 * @param form
296 310 * @param text
297 311 */
298 312 function showAsErrors(form, text) {
299 313 form.children('.form-errors').remove();
300 314
301 315 if (text.length > 0) {
302 316 var errorList = $('<div class="form-errors">' + text + '<div>');
303 317 errorList.appendTo(form);
304 318 }
305 319 }
306 320
307 321 /**
308 322 * Run js methods that are usually run on the document, on the new post
309 323 */
310 324 function processNewPost(post) {
311 325 addRefLinkPreview(post[0]);
312 326 highlightCode(post);
313 327 blink(post);
314 328 }
315 329
316 330 $(document).ready(function(){
317 331 if (initAutoupdate()) {
318 332 // Post form data over AJAX
319 333 var threadId = $('div.thread').children('.post').first().attr('id');
320 334
321 335 var form = $('#form');
322 336
323 337 if (form.length > 0) {
324 338 var options = {
325 339 beforeSubmit: function(arr, $form, options) {
326 340 showAsErrors($('form'), gettext('Sending message...'));
327 341 },
328 342 success: updateOnPost,
329 343 url: '/api/add_post/' + threadId + '/'
330 344 };
331 345
332 346 form.ajaxForm(options);
333 347
334 348 resetForm(form);
335 349 }
336 350 }
337 351
338 352 $('#autoupdate').click(getThreadDiff);
339 353 });
General Comments 0
You need to be logged in to leave comments. Login now