function send_flag_update(message, flag, op) { channel.post({ url: '/json/messages/flags', idempotent: true, data: { messages: JSON.stringify([message.id]), flag: flag, op: op, }, }); } exports._unread_batch_size = 1000; exports.send_read = (function () { let queue = []; let on_success; let start; function server_request() { // Wait for server IDs before sending flags const real_msgs = queue.filter(msg => !msg.locally_echoed); const real_msg_ids = real_msgs.map(msg => msg.id); if (real_msg_ids.length === 0) { setTimeout(start, 100); return; } const real_msg_ids_batch = real_msg_ids.slice(0, exports._unread_batch_size); // We have some real IDs. If there are any left in the queue when this // call finishes, they will be handled in the success callback. channel.post({ url: '/json/messages/flags', idempotent: true, data: {messages: JSON.stringify(real_msg_ids_batch), op: 'add', flag: 'read'}, success: on_success, }); } start = _.throttle(server_request, 1000); on_success = function on_success(data) { if (data === undefined || data.messages === undefined) { return; } queue = queue.filter(message => !data.messages.includes(message.id)); if (queue.length > 0) { start(); } }; function add(messages) { queue = queue.concat(messages); start(); } return add; }()); exports.save_collapsed = function (message) { send_flag_update(message, 'collapsed', 'add'); }; exports.save_uncollapsed = function (message) { send_flag_update(message, 'collapsed', 'remove'); }; // This updates the state of the starred flag in local data // structures, and triggers a UI rerender. exports.update_starred_flag = function (message_id, new_value) { const message = message_store.get(message_id); if (message === undefined) { // If we don't have the message locally, do nothing; if later // we fetch it, it'll come with the correct `starred` state. return; } message.starred = new_value; ui.update_starred_view(message_id, new_value); }; exports.toggle_starred_and_update_server = function (message) { if (message.locally_echoed) { // This is defensive code for when you hit the "*" key // before we get a server ack. It's rare that somebody // can star this quickly, and we don't have a good way // to tell the server which message was starred. return; } message.starred = !message.starred; // Unlike most calls to mark messages as read, we don't check // msg_list.can_mark_messages_read, because starring a message is an // explicit interaction and we'd like to preserve the user // expectation invariant that all starred messages are read. unread_ops.notify_server_message_read(message); ui.update_starred_view(message.id, message.starred); if (message.starred) { send_flag_update(message, 'starred', 'add'); starred_messages.add([message.id]); } else { send_flag_update(message, 'starred', 'remove'); starred_messages.remove([message.id]); } }; exports.unstar_all_messages = function () { const starred_msg_ids = starred_messages.get_starred_msg_ids(); channel.post({ url: '/json/messages/flags', idempotent: true, data: { messages: JSON.stringify(starred_msg_ids), flag: 'starred', op: 'remove', }, }); }; window.message_flags = exports;