2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2020-11-30 23:46:45 +01:00
|
|
|
const {strict: assert} = require("assert");
|
|
|
|
|
2023-02-22 23:04:10 +01:00
|
|
|
const {zrequire} = require("./lib/namespace");
|
|
|
|
const {run_test} = require("./lib/test");
|
|
|
|
const blueslip = require("./lib/zblueslip");
|
2020-12-01 00:02:16 +01:00
|
|
|
|
2022-08-14 15:33:13 +02:00
|
|
|
const user_topics = zrequire("user_topics");
|
2021-06-27 21:38:26 +02:00
|
|
|
const muted_users = zrequire("muted_users");
|
2023-02-22 23:03:47 +01:00
|
|
|
const {MessageListData} = zrequire("../src/message_list_data");
|
2021-05-05 13:08:49 +02:00
|
|
|
const {Filter} = zrequire("filter");
|
2020-12-01 23:21:38 +01:00
|
|
|
|
2018-05-05 13:34:31 +02:00
|
|
|
function make_msg(msg_id) {
|
|
|
|
return {
|
|
|
|
id: msg_id,
|
2021-01-25 06:54:00 +01:00
|
|
|
type: "stream",
|
2018-05-05 13:34:31 +02:00
|
|
|
unread: true,
|
2020-07-15 01:29:15 +02:00
|
|
|
topic: "whatever",
|
2018-05-05 13:34:31 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function make_msgs(msg_ids) {
|
2021-01-23 02:36:54 +01:00
|
|
|
return msg_ids.map((msg_id) => make_msg(msg_id));
|
2018-05-05 13:34:31 +02:00
|
|
|
}
|
|
|
|
|
2018-05-17 00:11:54 +02:00
|
|
|
function assert_contents(mld, msg_ids) {
|
|
|
|
const msgs = mld.all_messages();
|
|
|
|
assert.deepEqual(msgs, make_msgs(msg_ids));
|
|
|
|
}
|
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
function assert_msg_ids(messages, msg_ids) {
|
|
|
|
assert.deepEqual(
|
|
|
|
msg_ids,
|
|
|
|
messages.map((message) => message.id),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("basics", () => {
|
2018-05-05 13:34:31 +02:00
|
|
|
const mld = new MessageListData({
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics: false,
|
2023-05-10 17:54:31 +02:00
|
|
|
filter: new Filter(),
|
2018-05-05 13:34:31 +02:00
|
|
|
});
|
|
|
|
|
2023-11-12 00:13:40 +01:00
|
|
|
assert.equal(mld.is_keyword_search(), false);
|
2021-06-10 08:32:54 +02:00
|
|
|
assert.ok(mld.can_mark_messages_read());
|
2018-05-14 12:39:34 +02:00
|
|
|
mld.add_anywhere(make_msgs([35, 25, 15, 45]));
|
2018-05-05 13:34:31 +02:00
|
|
|
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [15, 25, 35, 45]);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
|
|
|
const new_msgs = make_msgs([10, 20, 30, 40, 50, 60, 70]);
|
2018-05-14 12:39:34 +02:00
|
|
|
const info = mld.add_messages(new_msgs);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
|
|
|
assert.deepEqual(info, {
|
|
|
|
top_messages: make_msgs([10]),
|
|
|
|
interior_messages: make_msgs([20, 30, 40]),
|
|
|
|
bottom_messages: make_msgs([50, 60, 70]),
|
|
|
|
});
|
|
|
|
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70]);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
|
|
|
assert.equal(mld.selected_id(), -1);
|
|
|
|
assert.equal(mld.closest_id(8), 10);
|
|
|
|
assert.equal(mld.closest_id(27), 25);
|
|
|
|
assert.equal(mld.closest_id(72), 70);
|
|
|
|
|
|
|
|
mld.set_selected_id(50);
|
|
|
|
assert.equal(mld.selected_id(), 50);
|
|
|
|
assert.equal(mld.selected_idx(), 8);
|
|
|
|
|
2020-11-12 22:03:45 +01:00
|
|
|
mld.remove([50]);
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [10, 15, 20, 25, 30, 35, 40, 45, 60, 70]);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
message lists: Don't allow user/topic mute message filtering independently.
This basically reverts 4bd7ec7c3699b08655fb3d6ae2a00a19c8a086db and
3a9dfc02e6414089de8ed5cbc85eb69f60454013.
The plan earlier was to have compeletely different codepaths
for user and topic muting, so that we could call seperate
functions in the message list class on receiving the respective
events.
However, this cannot be done, because if we, for example, on
receiving a `muted_users` event, filter `_all_items` based on
just user mutes, and store the result in `_items`, then, that
result may still contain topic-muted messages, which is
undesirable. Hence whenever we filter messages, we must do so
based on both user as well as topic muting.
(The code for the former will be added in further commits.)
So, we will have a single function which will handle updating
the message lists for muting.
2021-05-07 22:13:03 +02:00
|
|
|
mld.update_items_for_muting();
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [10, 15, 20, 25, 30, 35, 40, 45, 60, 70]);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
|
|
|
mld.reset_select_to_closest();
|
|
|
|
assert.equal(mld.selected_id(), 45);
|
|
|
|
assert.equal(mld.selected_idx(), 7);
|
|
|
|
|
|
|
|
assert.equal(mld.first_unread_message_id(), 10);
|
2022-02-16 01:30:23 +01:00
|
|
|
assert.equal(mld.has_unread_messages(), true);
|
2018-05-05 13:34:31 +02:00
|
|
|
mld.get(10).unread = false;
|
|
|
|
assert.equal(mld.first_unread_message_id(), 15);
|
2022-02-16 01:30:23 +01:00
|
|
|
assert.equal(mld.has_unread_messages(), true);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
2022-02-09 17:07:20 +01:00
|
|
|
mld.clear();
|
|
|
|
assert_contents(mld, []);
|
|
|
|
const msgs_sent_by_6 = [
|
|
|
|
{id: 2, sender_id: 6, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 4, sender_id: 6, type: "private", to_user_ids: "6,9,10"},
|
|
|
|
{id: 6, sender_id: 6, type: "private", to_user_ids: "6, 11"},
|
|
|
|
];
|
|
|
|
const msgs_with_sender_ids = [
|
|
|
|
{id: 1, sender_id: 1, type: "stream", stream_id: 1, topic: "random1"},
|
|
|
|
{id: 3, sender_id: 4, type: "stream", stream_id: 1, topic: "random2"},
|
|
|
|
{id: 5, sender_id: 2, type: "private", to_user_ids: "2,10,11"},
|
|
|
|
{id: 8, sender_id: 11, type: "private", to_user_ids: "10"},
|
|
|
|
{id: 9, sender_id: 11, type: "private", to_user_ids: "9"},
|
|
|
|
...msgs_sent_by_6,
|
|
|
|
];
|
|
|
|
mld.add_messages(msgs_with_sender_ids);
|
|
|
|
assert.deepEqual(mld.get_messages_sent_by_user(6), msgs_sent_by_6);
|
|
|
|
|
2018-05-05 13:34:31 +02:00
|
|
|
mld.clear();
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, []);
|
2018-05-05 13:34:31 +02:00
|
|
|
assert.equal(mld.closest_id(99), -1);
|
|
|
|
assert.equal(mld.get_last_message_sent_by_me(), undefined);
|
|
|
|
|
2018-05-14 12:39:34 +02:00
|
|
|
mld.add_messages(make_msgs([120, 125.01, 130, 140]));
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [120, 125.01, 130, 140]);
|
2018-05-05 13:34:31 +02:00
|
|
|
mld.set_selected_id(125.01);
|
|
|
|
assert.equal(mld.selected_id(), 125.01);
|
|
|
|
|
|
|
|
mld.get(125.01).id = 145;
|
2020-11-12 22:43:04 +01:00
|
|
|
mld.change_message_id(125.01, 145);
|
2018-05-17 00:11:54 +02:00
|
|
|
assert_contents(mld, [120, 130, 140, 145]);
|
2018-05-05 13:34:31 +02:00
|
|
|
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
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 { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
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);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.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;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
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-06 06:19:47 +01:00
|
|
|
for (const msg of mld.all_messages()) {
|
2018-05-05 13:34:31 +02:00
|
|
|
msg.unread = false;
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
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 { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
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);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.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;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
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-06 06:19:47 +01:00
|
|
|
}
|
2018-05-05 13:34:31 +02:00
|
|
|
|
|
|
|
assert.equal(mld.first_unread_message_id(), 145);
|
2022-02-16 01:30:23 +01:00
|
|
|
assert.equal(mld.has_unread_messages(), false);
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|
2018-05-05 13:34:31 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
run_test("muting", () => {
|
|
|
|
let mld = new MessageListData({
|
|
|
|
excludes_muted_topics: false,
|
2023-04-11 21:04:33 +02:00
|
|
|
filter: new Filter([{operator: "dm", operand: "alice@example.com"}]),
|
2021-01-25 06:54:00 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
const msgs = [
|
|
|
|
{id: 1, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
|
2023-06-16 17:37:19 +02:00
|
|
|
// mentions override muting
|
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
|
2021-05-07 22:34:38 +02:00
|
|
|
|
|
|
|
// 10 = muted user, 9 = non-muted user, 11 = you
|
2023-06-16 17:37:19 +02:00
|
|
|
// muted to group direct message
|
|
|
|
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
|
|
|
|
// non-muted to group direct message
|
|
|
|
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
|
|
|
|
// muted to 1:1 direct message
|
|
|
|
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
|
|
|
|
// non-muted to 1:1 direct message
|
|
|
|
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
|
|
|
|
// 1:1 direct message to muted
|
|
|
|
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
|
|
|
|
// 1:1 direct message to non-muted
|
|
|
|
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
|
2021-01-25 06:54:00 +01:00
|
|
|
];
|
|
|
|
|
2023-04-11 07:48:13 +02:00
|
|
|
user_topics.update_user_topics(1, "muted", user_topics.all_visibility_policies.MUTED);
|
2022-07-10 01:06:33 +02:00
|
|
|
muted_users.add_muted_user(10);
|
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
// `messages_filtered_for_topic_mutes` should skip filtering
|
|
|
|
// messages if `excludes_muted_topics` is false.
|
2022-07-10 01:06:33 +02:00
|
|
|
assert.deepEqual(mld.messages_filtered_for_topic_mutes(msgs), msgs);
|
2021-01-25 06:54:00 +01:00
|
|
|
|
2023-06-16 17:37:19 +02:00
|
|
|
// If we are in a 1:1 direct message narrow, `messages_filtered_for_user_mutes`
|
|
|
|
// should skip filtering messages.
|
2022-07-10 01:06:33 +02:00
|
|
|
assert.deepEqual(mld.messages_filtered_for_user_mutes(msgs), msgs);
|
2021-05-05 13:08:49 +02:00
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
// Test actual behaviour of `messages_filtered_for_*` methods.
|
2021-01-25 06:54:00 +01:00
|
|
|
mld.excludes_muted_topics = true;
|
2021-05-05 13:08:49 +02:00
|
|
|
mld.filter = new Filter([{operator: "stream", operand: "general"}]);
|
2021-01-25 06:54:00 +01:00
|
|
|
const res = mld.messages_filtered_for_topic_mutes(msgs);
|
|
|
|
assert.deepEqual(res, [
|
|
|
|
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true}, // mentions override muting
|
2021-05-07 22:34:38 +02:00
|
|
|
|
2023-06-16 17:37:19 +02:00
|
|
|
// `messages_filtered_for_topic_mutes` does not affect direct messages
|
2021-05-07 22:34:38 +02:00
|
|
|
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
|
|
|
|
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
|
|
|
|
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
|
|
|
|
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
|
|
|
|
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
|
|
|
|
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
|
|
|
|
]);
|
|
|
|
|
|
|
|
const res_user = mld.messages_filtered_for_user_mutes(msgs);
|
|
|
|
assert.deepEqual(res_user, [
|
|
|
|
// `messages_filtered_for_user_mutes` does not affect stream messages
|
|
|
|
{id: 1, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
|
2023-06-16 17:37:19 +02:00
|
|
|
// muted to group direct message
|
|
|
|
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
|
|
|
|
// non-muted to group direct message
|
|
|
|
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
|
|
|
|
// non-muted to 1:1 direct message
|
|
|
|
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
|
|
|
|
// 1:1 direct message to non-muted
|
|
|
|
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
|
2021-01-25 06:54:00 +01:00
|
|
|
]);
|
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
// Output filtered based on both topic and user muting.
|
|
|
|
mld._all_items = msgs;
|
|
|
|
const filtered_messages = mld.unmuted_messages(mld._all_items);
|
|
|
|
assert.deepEqual(filtered_messages, [
|
|
|
|
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
|
|
|
|
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
|
|
|
|
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
|
|
|
|
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
|
|
|
|
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Also verify that, the correct set of messages is stored in `_items`
|
|
|
|
// once we update the list for muting.
|
|
|
|
mld.update_items_for_muting();
|
|
|
|
assert.deepEqual(filtered_messages, mld._items);
|
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
// MessageListData methods should always attempt to filter messages,
|
2021-05-07 22:34:38 +02:00
|
|
|
// and keep `_all_items` up-to-date.
|
2021-01-25 06:54:00 +01:00
|
|
|
mld = new MessageListData({
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics: true,
|
2023-05-10 17:54:31 +02:00
|
|
|
filter: new Filter(),
|
2018-05-17 00:11:54 +02:00
|
|
|
});
|
2021-01-25 06:54:00 +01:00
|
|
|
assert.deepEqual(mld._all_items, []);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
let unmuted_messages_calls = 0;
|
|
|
|
mld.unmuted_messages = (messages) => {
|
|
|
|
unmuted_messages_calls = unmuted_messages_calls + 1;
|
2021-01-25 06:54:00 +01:00
|
|
|
return messages;
|
2020-07-15 00:34:28 +02:00
|
|
|
};
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
mld.add_anywhere([{id: 10}, {id: 20}]);
|
|
|
|
assert.equal(unmuted_messages_calls, 1);
|
|
|
|
assert_msg_ids(mld._all_items, [10, 20]);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
mld.prepend([{id: 9}, {id: 19}]);
|
|
|
|
assert.equal(unmuted_messages_calls, 2);
|
|
|
|
assert_msg_ids(mld._all_items, [9, 19, 10, 20]);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-05-07 22:34:38 +02:00
|
|
|
mld.append([{id: 11}, {id: 21}]);
|
|
|
|
assert.equal(unmuted_messages_calls, 3);
|
|
|
|
assert_msg_ids(mld._all_items, [9, 19, 10, 20, 11, 21]);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
mld.remove([9]);
|
2021-05-07 22:34:38 +02:00
|
|
|
assert_msg_ids(mld._all_items, [19, 10, 20, 11, 21]);
|
|
|
|
|
|
|
|
mld.reorder_messages(20);
|
|
|
|
assert_msg_ids(mld._all_items, [10, 11, 19, 20, 21]);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
mld.clear();
|
|
|
|
assert_msg_ids(mld._all_items, []);
|
2018-06-04 22:14:39 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
// Test `add_messages` populates the `info` dict **after**
|
|
|
|
// filtering the messages.
|
|
|
|
mld = new MessageListData({
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics: true,
|
2023-05-10 17:54:31 +02:00
|
|
|
filter: new Filter(),
|
2018-06-04 22:14:39 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
const orig_messages = [
|
2021-01-25 06:54:00 +01:00
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 4, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 7, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 8, type: "stream", stream_id: 1, topic: "whatever"},
|
2018-06-04 22:14:39 +02:00
|
|
|
];
|
|
|
|
|
2018-06-04 22:39:25 +02:00
|
|
|
const orig_info = mld.add_messages(orig_messages);
|
|
|
|
assert.deepEqual(orig_info, {
|
|
|
|
top_messages: [],
|
|
|
|
interior_messages: [],
|
|
|
|
bottom_messages: [
|
2021-01-25 06:54:00 +01:00
|
|
|
{id: 4, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 8, type: "stream", stream_id: 1, topic: "whatever"},
|
2018-06-04 22:39:25 +02:00
|
|
|
],
|
|
|
|
});
|
2018-06-04 22:14:39 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
assert_msg_ids(mld._all_items, [3, 4, 7, 8]);
|
|
|
|
assert_msg_ids(mld._items, [4, 8]);
|
2018-06-04 22:14:39 +02:00
|
|
|
|
|
|
|
const more_messages = [
|
2021-01-25 06:54:00 +01:00
|
|
|
{id: 1, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 3, type: "stream", stream_id: 1, topic: "muted"}, // dup
|
|
|
|
{id: 5, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 6, type: "stream", stream_id: 1, topic: "whatever"},
|
|
|
|
{id: 9, type: "stream", stream_id: 1, topic: "muted"},
|
|
|
|
{id: 10, type: "stream", stream_id: 1, topic: "whatever"},
|
2018-06-04 22:14:39 +02:00
|
|
|
];
|
2018-06-04 22:39:25 +02:00
|
|
|
|
|
|
|
const more_info = mld.add_messages(more_messages);
|
2018-06-04 22:14:39 +02:00
|
|
|
|
2021-01-25 06:54:00 +01:00
|
|
|
assert_msg_ids(mld._all_items, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
assert_msg_ids(mld._items, [2, 4, 6, 8, 10]);
|
2018-06-04 22:14:39 +02:00
|
|
|
|
2018-06-04 22:39:25 +02:00
|
|
|
assert.deepEqual(more_info, {
|
2021-01-25 06:54:00 +01:00
|
|
|
top_messages: [{id: 2, type: "stream", stream_id: 1, topic: "whatever"}],
|
|
|
|
interior_messages: [{id: 6, type: "stream", stream_id: 1, topic: "whatever"}],
|
|
|
|
bottom_messages: [{id: 10, type: "stream", stream_id: 1, topic: "whatever"}],
|
2018-06-04 22:39:25 +02:00
|
|
|
});
|
2018-06-04 22:14:39 +02:00
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
run_test("errors", () => {
|
2018-05-05 13:34:31 +02:00
|
|
|
const mld = new MessageListData({
|
2021-01-25 06:23:16 +01:00
|
|
|
excludes_muted_topics: false,
|
2023-05-10 17:54:31 +02:00
|
|
|
filter: new Filter(),
|
2018-05-05 13:34:31 +02:00
|
|
|
});
|
2020-07-15 01:29:15 +02:00
|
|
|
assert.equal(mld.get("bogus-id"), undefined);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
assert.throws(
|
|
|
|
() => {
|
|
|
|
mld._add_to_hash(["asdf"]);
|
|
|
|
},
|
|
|
|
{message: "Bad message id"},
|
|
|
|
);
|
2018-05-17 00:11:54 +02:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
blueslip.expect("error", "Duplicate message added to MessageListData");
|
|
|
|
mld._hash.set(1, "taken");
|
2018-05-17 00:11:54 +02:00
|
|
|
mld._add_to_hash(make_msgs([1]));
|
2018-05-15 12:40:07 +02:00
|
|
|
});
|