zulip/static/js/message_store.js

143 lines
4.5 KiB
JavaScript
Raw Normal View History

import * as blueslip from "./blueslip";
import * as people from "./people";
const stored_messages = new Map();
export function update_message_cache(message) {
// You should only call this from message_helper (or in tests).
stored_messages.set(message.id, message);
}
export function get_cached_message(message_id) {
// You should only call this from message_helper.
// Use the get() wrapper below for most other use cases.
return stored_messages.get(message_id);
}
export function clear_for_testing() {
stored_messages.clear();
}
export function get(message_id) {
if (message_id === undefined || message_id === null) {
blueslip.error("message_store.get got bad value: " + message_id);
return undefined;
}
if (typeof message_id !== "number") {
blueslip.error("message_store got non-number: " + message_id);
// Try to soldier on, assuming the caller treats message
// ids as strings.
message_id = Number.parseFloat(message_id);
}
return stored_messages.get(message_id);
}
export function get_pm_emails(message) {
const user_ids = people.pm_with_user_ids(message);
const emails = user_ids
.map((user_id) => {
const person = people.get_by_user_id(user_id);
if (!person) {
blueslip.error("Unknown user id " + user_id);
return "?";
}
return person.email;
})
.sort();
return emails.join(", ");
}
export function get_pm_full_names(message) {
const user_ids = people.pm_with_user_ids(message);
const names = people.get_display_full_names(user_ids).sort();
return names.join(", ");
}
export function set_message_booleans(message) {
const flags = message.flags || [];
function convert_flag(flag_name) {
js: Convert a.indexOf(…) !== -1 to a.includes(…). Babel polyfills this for us for Internet Explorer. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import K from "ast-types/gen/kinds"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; recast.visit(ast, { visitBinaryExpression(path) { const { operator, left, right } = path.node; if ( n.CallExpression.check(left) && n.MemberExpression.check(left.callee) && !left.callee.computed && n.Identifier.check(left.callee.property) && left.callee.property.name === "indexOf" && left.arguments.length === 1 && checkExpression(left.arguments[0]) && ((["===", "!==", "==", "!=", ">", "<="].includes(operator) && n.UnaryExpression.check(right) && right.operator == "-" && n.Literal.check(right.argument) && right.argument.value === 1) || ([">=", "<"].includes(operator) && n.Literal.check(right) && right.value === 0)) ) { const test = b.callExpression( b.memberExpression(left.callee.object, b.identifier("includes")), [left.arguments[0]] ); path.replace( ["!==", "!=", ">", ">="].includes(operator) ? test : b.unaryExpression("!", test) ); changed = true; } this.traverse(path); }, }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
return flags.includes(flag_name);
}
message.unread = !convert_flag("read");
message.historical = convert_flag("historical");
message.starred = convert_flag("starred");
message.mentioned = convert_flag("mentioned") || convert_flag("wildcard_mentioned");
message.mentioned_me_directly = convert_flag("mentioned");
message.collapsed = convert_flag("collapsed");
message.alerted = convert_flag("has_alert_word");
// Once we have set boolean flags here, the `flags` attribute is
// just a distraction, so we delete it. (All the downstream code
// uses booleans.)
delete message.flags;
}
export function init_booleans(message) {
// This initializes booleans for the local-echo path where
// we don't have flags from the server yet. (We want to
// explicitly set flags to false to be consistent with other
// codepaths.)
message.unread = false;
message.historical = false;
message.starred = false;
message.mentioned = false;
message.mentioned_me_directly = false;
message.collapsed = false;
message.alerted = false;
}
export function update_booleans(message, flags) {
// When we get server flags for local echo or message edits,
// we are vulnerable to race conditions, so only update flags
// that are driven by message content.
function convert_flag(flag_name) {
js: Convert a.indexOf(…) !== -1 to a.includes(…). Babel polyfills this for us for Internet Explorer. import * as babelParser from "recast/parsers/babel"; import * as recast from "recast"; import * as tsParser from "recast/parsers/typescript"; import { builders as b, namedTypes as n } from "ast-types"; import K from "ast-types/gen/kinds"; import fs from "fs"; import path from "path"; import process from "process"; const checkExpression = (node: n.Node): node is K.ExpressionKind => n.Expression.check(node); for (const file of process.argv.slice(2)) { console.log("Parsing", file); const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), { parser: path.extname(file) === ".ts" ? tsParser : babelParser, }); let changed = false; recast.visit(ast, { visitBinaryExpression(path) { const { operator, left, right } = path.node; if ( n.CallExpression.check(left) && n.MemberExpression.check(left.callee) && !left.callee.computed && n.Identifier.check(left.callee.property) && left.callee.property.name === "indexOf" && left.arguments.length === 1 && checkExpression(left.arguments[0]) && ((["===", "!==", "==", "!=", ">", "<="].includes(operator) && n.UnaryExpression.check(right) && right.operator == "-" && n.Literal.check(right.argument) && right.argument.value === 1) || ([">=", "<"].includes(operator) && n.Literal.check(right) && right.value === 0)) ) { const test = b.callExpression( b.memberExpression(left.callee.object, b.identifier("includes")), [left.arguments[0]] ); path.replace( ["!==", "!=", ">", ">="].includes(operator) ? test : b.unaryExpression("!", test) ); changed = true; } this.traverse(path); }, }); if (changed) { console.log("Writing", file); fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" }); } } Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
return flags.includes(flag_name);
}
message.mentioned = convert_flag("mentioned") || convert_flag("wildcard_mentioned");
message.mentioned_me_directly = convert_flag("mentioned");
message.alerted = convert_flag("has_alert_word");
}
export function update_property(property, value, info) {
switch (property) {
case "sender_full_name":
case "small_avatar_url":
for (const msg of stored_messages.values()) {
if (msg.sender_id && msg.sender_id === info.user_id) {
msg[property] = value;
}
}
break;
case "stream_name":
for (const msg of stored_messages.values()) {
if (msg.stream_id && msg.stream_id === info.stream_id) {
msg.display_recipient = value;
msg.stream = value;
}
}
break;
case "status_emoji_info":
for (const msg of stored_messages.values()) {
if (msg.sender_id && msg.sender_id === info.user_id) {
msg[property] = value;
}
}
break;
}
}
export function reify_message_id({old_id, new_id}) {
if (stored_messages.has(old_id)) {
stored_messages.set(new_id, stored_messages.get(old_id));
stored_messages.delete(old_id);
}
}