2019-11-02 00:06:25 +01:00
|
|
|
const render_group_pms = require('../templates/group_pms.hbs');
|
2019-02-08 11:56:33 +01:00
|
|
|
|
2013-02-07 19:57:45 +01:00
|
|
|
/*
|
|
|
|
Helpers for detecting user activity and managing user idle states
|
|
|
|
*/
|
|
|
|
|
2013-08-20 20:57:26 +02:00
|
|
|
/* Broadcast "idle" to server after 5 minutes of local inactivity */
|
2019-11-02 00:06:25 +01:00
|
|
|
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
|
2013-03-05 17:11:55 +01:00
|
|
|
/* Time between keep-alive pings */
|
2019-11-02 00:06:25 +01:00
|
|
|
const ACTIVE_PING_INTERVAL_MS = 50 * 1000;
|
2013-02-12 20:40:28 +01:00
|
|
|
|
2016-04-03 08:01:10 +02:00
|
|
|
/* Keep in sync with views.py:update_active_status_backend() */
|
2013-08-20 21:18:56 +02:00
|
|
|
exports.ACTIVE = "active";
|
|
|
|
exports.IDLE = "idle";
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2019-06-29 02:52:15 +02:00
|
|
|
// When you open Zulip in a new browser window, client_is_active
|
|
|
|
// should be true. When a server-initiated reload happens, however,
|
|
|
|
// it should be initialized to false. We handle this with a check for
|
|
|
|
// whether the window is focused at initialization time.
|
|
|
|
exports.client_is_active = document.hasFocus && document.hasFocus();
|
|
|
|
|
|
|
|
// new_user_input is a more strict version of client_is_active used
|
|
|
|
// primarily for analytics. We initialize this to true, to count new
|
|
|
|
// page loads, but set it to false in the onload function in reload.js
|
|
|
|
// if this was a server-initiated-reload to avoid counting a
|
|
|
|
// server-initiated reload as user activity.
|
2013-09-06 21:52:12 +02:00
|
|
|
exports.new_user_input = true;
|
|
|
|
|
2020-02-03 09:39:58 +01:00
|
|
|
const huddle_timestamps = new Map();
|
2013-10-23 20:44:31 +02:00
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function update_pm_count_in_dom(count_span, value_span, count) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const li = count_span.parents('li');
|
2017-05-24 20:00:04 +02:00
|
|
|
|
2016-11-11 20:48:13 +01:00
|
|
|
if (count === 0) {
|
|
|
|
count_span.hide();
|
2017-05-24 20:00:04 +02:00
|
|
|
li.removeClass("user-with-count");
|
2016-11-11 20:48:13 +01:00
|
|
|
value_span.text('');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
count_span.show();
|
2017-05-24 20:00:04 +02:00
|
|
|
li.addClass("user-with-count");
|
|
|
|
value_span.text(count);
|
|
|
|
}
|
2016-11-11 20:48:13 +01:00
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function update_group_count_in_dom(count_span, value_span, count) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const li = count_span.parent();
|
2017-05-24 20:00:04 +02:00
|
|
|
|
|
|
|
if (count === 0) {
|
|
|
|
count_span.hide();
|
|
|
|
li.removeClass("group-with-count");
|
|
|
|
value_span.text('');
|
|
|
|
return;
|
2016-11-11 20:48:13 +01:00
|
|
|
}
|
2017-05-24 20:00:04 +02:00
|
|
|
|
|
|
|
count_span.show();
|
|
|
|
li.addClass("group-with-count");
|
2016-11-11 20:48:13 +01:00
|
|
|
value_span.text(count);
|
|
|
|
}
|
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function get_pm_list_item(user_id) {
|
2018-04-19 14:17:22 +02:00
|
|
|
return buddy_list.find_li({
|
|
|
|
key: user_id,
|
|
|
|
});
|
2017-03-17 19:38:24 +01:00
|
|
|
}
|
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function get_group_list_item(user_ids_string) {
|
2016-12-02 21:34:35 +01:00
|
|
|
return $("li.group-pms-sidebar-entry[data-user-ids='" + user_ids_string + "']");
|
2016-11-11 20:48:13 +01:00
|
|
|
}
|
|
|
|
|
2017-05-24 20:00:04 +02:00
|
|
|
function set_pm_count(user_ids_string, count) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const count_span = get_pm_list_item(user_ids_string).find('.count');
|
|
|
|
const value_span = count_span.find('.value');
|
2017-05-24 20:00:04 +02:00
|
|
|
update_pm_count_in_dom(count_span, value_span, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
function set_group_count(user_ids_string, count) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const count_span = get_group_list_item(user_ids_string).find('.count');
|
|
|
|
const value_span = count_span.find('.value');
|
2017-05-24 20:00:04 +02:00
|
|
|
update_group_count_in_dom(count_span, value_span, count);
|
2016-11-11 20:48:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.update_dom_with_unread_counts = function (counts) {
|
|
|
|
// counts is just a data object that gets calculated elsewhere
|
|
|
|
// Our job is to update some DOM elements.
|
|
|
|
|
2020-02-03 09:26:53 +01:00
|
|
|
for (const [user_ids_string, count] of counts.pm_count) {
|
2016-11-16 00:09:09 +01:00
|
|
|
// TODO: just use user_ids_string in our markup
|
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
|
|
|
const is_pm = !user_ids_string.includes(',');
|
2017-05-24 20:00:04 +02:00
|
|
|
if (is_pm) {
|
|
|
|
set_pm_count(user_ids_string, count);
|
|
|
|
} else {
|
|
|
|
set_group_count(user_ids_string, count);
|
|
|
|
}
|
2020-02-03 09:26:53 +01:00
|
|
|
}
|
2016-11-11 20:48:13 +01:00
|
|
|
};
|
2014-02-27 01:46:17 +01:00
|
|
|
|
2013-10-23 20:44:31 +02:00
|
|
|
exports.process_loaded_messages = function (messages) {
|
2019-11-02 00:06:25 +01:00
|
|
|
let need_resize = false;
|
2016-11-11 19:30:04 +01: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 message of messages) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const huddle_string = people.huddle_string(message);
|
2016-11-17 23:16:29 +01:00
|
|
|
|
2017-02-05 00:22:16 +01:00
|
|
|
if (huddle_string) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const old_timestamp = huddle_timestamps.get(huddle_string);
|
2016-11-17 23:16:29 +01:00
|
|
|
|
2018-06-06 18:50:09 +02:00
|
|
|
if (!old_timestamp || old_timestamp < message.timestamp) {
|
2017-02-05 00:22:16 +01:00
|
|
|
huddle_timestamps.set(huddle_string, message.timestamp);
|
|
|
|
need_resize = true;
|
2013-10-23 20:44: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
|
|
|
}
|
2016-11-11 18:00:09 +01:00
|
|
|
|
|
|
|
exports.update_huddles();
|
2016-11-11 19:30:04 +01:00
|
|
|
|
|
|
|
if (need_resize) {
|
|
|
|
resize.resize_page_components(); // big hammer
|
|
|
|
}
|
2013-10-23 20:44:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_huddles = function () {
|
2020-02-04 23:46:56 +01:00
|
|
|
let huddles = Array.from(huddle_timestamps.keys());
|
2013-10-23 20:44:31 +02:00
|
|
|
huddles = _.sortBy(huddles, function (huddle) {
|
|
|
|
return huddle_timestamps.get(huddle);
|
|
|
|
});
|
|
|
|
return huddles.reverse();
|
|
|
|
};
|
|
|
|
|
2019-12-29 15:07:05 +01:00
|
|
|
function huddle_split(huddle) {
|
js: Convert _.map(a, …) to a.map(…).
And convert the corresponding function expressions to arrow style
while we’re here.
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, {
visitCallExpression(path) {
const { callee, arguments: args } = path.node;
if (
n.MemberExpression.check(callee) &&
!callee.computed &&
n.Identifier.check(callee.object) &&
callee.object.name === "_" &&
n.Identifier.check(callee.property) &&
callee.property.name === "map" &&
args.length === 2 &&
checkExpression(args[0]) &&
checkExpression(args[1])
) {
const [arr, fn] = args;
path.replace(
b.callExpression(b.memberExpression(arr, b.identifier("map")), [
n.FunctionExpression.check(fn) ||
n.ArrowFunctionExpression.check(fn)
? b.arrowFunctionExpression(
fn.params,
n.BlockStatement.check(fn.body) &&
fn.body.body.length === 1 &&
n.ReturnStatement.check(fn.body.body[0])
? fn.body.body[0].argument || b.identifier("undefined")
: fn.body
)
: fn,
])
);
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 02:43:49 +01:00
|
|
|
return huddle.split(',').map(s => parseInt(s, 10));
|
2019-12-29 15:07:05 +01:00
|
|
|
}
|
|
|
|
|
2013-10-23 22:22:34 +02:00
|
|
|
exports.full_huddle_name = function (huddle) {
|
2019-12-29 15:07:05 +01:00
|
|
|
const user_ids = huddle_split(huddle);
|
2013-10-23 22:22:34 +02:00
|
|
|
|
js: Convert _.map(a, …) to a.map(…).
And convert the corresponding function expressions to arrow style
while we’re here.
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, {
visitCallExpression(path) {
const { callee, arguments: args } = path.node;
if (
n.MemberExpression.check(callee) &&
!callee.computed &&
n.Identifier.check(callee.object) &&
callee.object.name === "_" &&
n.Identifier.check(callee.property) &&
callee.property.name === "map" &&
args.length === 2 &&
checkExpression(args[0]) &&
checkExpression(args[1])
) {
const [arr, fn] = args;
path.replace(
b.callExpression(b.memberExpression(arr, b.identifier("map")), [
n.FunctionExpression.check(fn) ||
n.ArrowFunctionExpression.check(fn)
? b.arrowFunctionExpression(
fn.params,
n.BlockStatement.check(fn.body) &&
fn.body.body.length === 1 &&
n.ReturnStatement.check(fn.body.body[0])
? fn.body.body[0].argument || b.identifier("undefined")
: fn.body
)
: fn,
])
);
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 02:43:49 +01:00
|
|
|
const names = user_ids.map(user_id => {
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(user_id);
|
2016-11-17 23:16:29 +01:00
|
|
|
return person.full_name;
|
2013-10-23 22:22:34 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
return names.join(', ');
|
|
|
|
};
|
|
|
|
|
2013-11-04 22:59:03 +01:00
|
|
|
exports.short_huddle_name = function (huddle) {
|
2019-12-29 15:07:05 +01:00
|
|
|
const user_ids = huddle_split(huddle);
|
2013-11-04 22:59:03 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const num_to_show = 3;
|
js: Convert _.map(a, …) to a.map(…).
And convert the corresponding function expressions to arrow style
while we’re here.
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, {
visitCallExpression(path) {
const { callee, arguments: args } = path.node;
if (
n.MemberExpression.check(callee) &&
!callee.computed &&
n.Identifier.check(callee.object) &&
callee.object.name === "_" &&
n.Identifier.check(callee.property) &&
callee.property.name === "map" &&
args.length === 2 &&
checkExpression(args[0]) &&
checkExpression(args[1])
) {
const [arr, fn] = args;
path.replace(
b.callExpression(b.memberExpression(arr, b.identifier("map")), [
n.FunctionExpression.check(fn) ||
n.ArrowFunctionExpression.check(fn)
? b.arrowFunctionExpression(
fn.params,
n.BlockStatement.check(fn.body) &&
fn.body.body.length === 1 &&
n.ReturnStatement.check(fn.body.body[0])
? fn.body.body[0].argument || b.identifier("undefined")
: fn.body
)
: fn,
])
);
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 02:43:49 +01:00
|
|
|
let names = user_ids.map(user_id => {
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(user_id);
|
2016-11-17 23:16:29 +01:00
|
|
|
return person.full_name;
|
2013-11-04 22:59:03 +01:00
|
|
|
});
|
2016-11-23 17:25:43 +01:00
|
|
|
|
|
|
|
names = _.sortBy(names, function (name) { return name.toLowerCase(); });
|
|
|
|
names = names.slice(0, num_to_show);
|
2019-11-02 00:06:25 +01:00
|
|
|
const others = user_ids.length - num_to_show;
|
2013-11-04 22:59:03 +01:00
|
|
|
|
|
|
|
if (others === 1) {
|
|
|
|
names.push("+ 1 other");
|
|
|
|
} else if (others >= 2) {
|
|
|
|
names.push("+ " + others + " others");
|
|
|
|
}
|
|
|
|
|
|
|
|
return names.join(', ');
|
|
|
|
};
|
|
|
|
|
2019-06-29 02:50:32 +02:00
|
|
|
function mark_client_idle() {
|
2017-03-06 15:42:58 +01:00
|
|
|
// When we become idle, we don't immediately send anything to the
|
|
|
|
// server; instead, we wait for our next periodic update, since
|
|
|
|
// this data is fundamentally not timely.
|
2019-06-29 02:52:15 +02:00
|
|
|
exports.client_is_active = false;
|
2013-02-07 19:57:45 +01:00
|
|
|
}
|
|
|
|
|
2019-01-25 19:20:51 +01:00
|
|
|
exports.redraw_user = function (user_id) {
|
2017-02-28 23:34:14 +01:00
|
|
|
if (page_params.realm_presence_disabled) {
|
2016-07-27 02:09:10 +02:00
|
|
|
return;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const filter_text = exports.get_filter_text();
|
2018-04-19 15:46:56 +02:00
|
|
|
|
|
|
|
if (!buddy_data.matches_filter(filter_text, user_id)) {
|
2017-02-21 00:53:08 +01:00
|
|
|
return;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
2017-02-21 00:53:08 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const info = buddy_data.get_item(user_id);
|
2018-04-19 15:46:56 +02:00
|
|
|
|
2018-04-19 14:17:22 +02:00
|
|
|
buddy_list.insert_or_move({
|
|
|
|
key: user_id,
|
|
|
|
item: info,
|
|
|
|
});
|
2017-02-21 00:53:08 +01:00
|
|
|
};
|
|
|
|
|
2018-04-19 17:47:41 +02:00
|
|
|
exports.searching = function () {
|
|
|
|
return exports.user_filter && exports.user_filter.searching();
|
|
|
|
};
|
|
|
|
|
2017-02-21 00:53:08 +01:00
|
|
|
exports.build_user_sidebar = function () {
|
2017-02-28 23:34:14 +01:00
|
|
|
if (page_params.realm_presence_disabled) {
|
2017-02-21 00:53:08 +01:00
|
|
|
return;
|
2016-01-19 21:03:56 +01:00
|
|
|
}
|
2013-10-23 19:02:18 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const filter_text = exports.get_filter_text();
|
2017-10-03 03:18:49 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_ids = buddy_data.get_filtered_and_sorted_user_ids(filter_text);
|
2018-04-19 14:17:22 +02:00
|
|
|
|
2020-01-15 15:05:44 +01:00
|
|
|
const finish = blueslip.start_timing('buddy_list.populate');
|
2018-04-19 14:17:22 +02:00
|
|
|
buddy_list.populate({
|
2018-07-14 14:06:30 +02:00
|
|
|
keys: user_ids,
|
2018-04-19 14:17:22 +02:00
|
|
|
});
|
2020-01-15 15:05:44 +01:00
|
|
|
finish();
|
2017-02-21 00:53:08 +01:00
|
|
|
|
2017-02-26 20:00:17 +01:00
|
|
|
resize.resize_page_components();
|
|
|
|
|
2018-07-14 14:06:30 +02:00
|
|
|
return user_ids; // for testing
|
2016-03-20 07:03:00 +01:00
|
|
|
};
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
function do_update_users_for_search() {
|
2019-02-08 14:37:04 +01:00
|
|
|
// Hide all the popovers but not userlist sidebar
|
|
|
|
// when the user is searching.
|
2019-07-10 08:03:41 +02:00
|
|
|
popovers.hide_all_except_sidebars();
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.build_user_sidebar();
|
|
|
|
exports.user_cursor.reset();
|
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const update_users_for_search = _.throttle(do_update_users_for_search, 50);
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2016-12-05 07:02:18 +01:00
|
|
|
function show_huddles() {
|
2017-01-04 19:34:55 +01:00
|
|
|
$('#group-pm-list').addClass("show");
|
2016-11-11 19:30:04 +01:00
|
|
|
}
|
|
|
|
|
2016-12-05 07:02:18 +01:00
|
|
|
function hide_huddles() {
|
2017-01-04 19:34:55 +01:00
|
|
|
$('#group-pm-list').removeClass("show");
|
2016-11-11 19:30:04 +01:00
|
|
|
}
|
|
|
|
|
2013-10-18 00:56:49 +02:00
|
|
|
exports.update_huddles = function () {
|
2017-02-28 23:34:14 +01:00
|
|
|
if (page_params.realm_presence_disabled) {
|
2016-07-27 02:09:10 +02:00
|
|
|
return;
|
2013-11-28 22:13:01 +01:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const huddles = exports.get_huddles().slice(0, 10);
|
2013-10-18 00:56:49 +02:00
|
|
|
|
2013-12-03 16:44:42 +01:00
|
|
|
if (huddles.length === 0) {
|
2016-11-11 19:30:04 +01:00
|
|
|
hide_huddles();
|
2013-10-18 00:56:49 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
js: Convert _.map(a, …) to a.map(…).
And convert the corresponding function expressions to arrow style
while we’re here.
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, {
visitCallExpression(path) {
const { callee, arguments: args } = path.node;
if (
n.MemberExpression.check(callee) &&
!callee.computed &&
n.Identifier.check(callee.object) &&
callee.object.name === "_" &&
n.Identifier.check(callee.property) &&
callee.property.name === "map" &&
args.length === 2 &&
checkExpression(args[0]) &&
checkExpression(args[1])
) {
const [arr, fn] = args;
path.replace(
b.callExpression(b.memberExpression(arr, b.identifier("map")), [
n.FunctionExpression.check(fn) ||
n.ArrowFunctionExpression.check(fn)
? b.arrowFunctionExpression(
fn.params,
n.BlockStatement.check(fn.body) &&
fn.body.body.length === 1 &&
n.ReturnStatement.check(fn.body.body[0])
? fn.body.body[0].argument || b.identifier("undefined")
: fn.body
)
: fn,
])
);
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 02:43:49 +01:00
|
|
|
const group_pms = huddles.map(huddle => ({
|
|
|
|
user_ids_string: huddle,
|
|
|
|
name: exports.full_huddle_name(huddle),
|
|
|
|
href: hash_util.huddle_with_uri(huddle),
|
|
|
|
fraction_present: buddy_data.huddle_fraction_present(huddle),
|
|
|
|
short_name: exports.short_huddle_name(huddle),
|
|
|
|
}));
|
2013-10-18 00:56:49 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const html = render_group_pms({group_pms: group_pms});
|
2019-03-01 01:40:05 +01:00
|
|
|
ui.get_content_element($('#group-pms')).html(html);
|
2013-11-08 00:53:12 +01: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 user_ids_string of huddles) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const count = unread.num_unread_for_person(user_ids_string);
|
2017-05-24 20:00:04 +02:00
|
|
|
set_group_count(user_ids_string, count);
|
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
|
|
|
}
|
2013-11-08 00:53:12 +01:00
|
|
|
|
2016-11-11 19:30:04 +01:00
|
|
|
show_huddles();
|
2013-10-18 00:56:49 +02:00
|
|
|
};
|
|
|
|
|
2019-03-29 00:13:07 +01:00
|
|
|
exports.compute_active_status = function () {
|
|
|
|
// The overall algorithm intent for the `status` field is to send
|
|
|
|
// `ACTIVE` (aka green circle) if we know the user is at their
|
|
|
|
// computer, and IDLE (aka orange circle) if the user might not
|
|
|
|
// be:
|
|
|
|
//
|
|
|
|
// * For the webapp, we just know whether this window has focus.
|
|
|
|
// * For the electron desktop app, we also know whether the
|
|
|
|
// user is active or idle elsewhere on their system.
|
|
|
|
//
|
|
|
|
// The check for `idle_on_system === undefined` is feature
|
|
|
|
// detection; older desktop app releases never set that property.
|
|
|
|
if (window.electron_bridge !== undefined
|
|
|
|
&& window.electron_bridge.idle_on_system !== undefined) {
|
|
|
|
if (window.electron_bridge.idle_on_system) {
|
|
|
|
return exports.IDLE;
|
|
|
|
}
|
|
|
|
return exports.ACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exports.client_is_active) {
|
|
|
|
return exports.ACTIVE;
|
|
|
|
}
|
|
|
|
return exports.IDLE;
|
|
|
|
};
|
|
|
|
|
2019-06-29 02:53:22 +02:00
|
|
|
function send_presence_to_server(want_redraw) {
|
2020-02-13 21:22:05 +01:00
|
|
|
// Zulip has 2 data feeds coming from the server to the client:
|
|
|
|
// The server_events data, and this presence feed. Data from
|
|
|
|
// server_events is nicely serialized, but if we've been offline
|
|
|
|
// and not running for a while (e.g. due to suspend), we can end
|
|
|
|
// up with inconsistent state where users appear in presence that
|
|
|
|
// don't appear in people.js. We handle this in 2 stages. First,
|
|
|
|
// here, we trigger an extra run of the clock-jump check that
|
|
|
|
// detects whether this device just resumed from suspend. This
|
|
|
|
// ensures that server_events.suspect_offline is always up-to-date
|
|
|
|
// before we initiate a presence request.
|
|
|
|
//
|
|
|
|
// If we did just resume, it will also trigger an immediate
|
|
|
|
// server_events request to the server (the success handler to
|
|
|
|
// which will clear suspect_offline and potentially trigger a
|
|
|
|
// reload if the device was offline for more than
|
|
|
|
// DEFAULT_EVENT_QUEUE_TIMEOUT_SECS).
|
|
|
|
server_events.check_for_unsuspend();
|
|
|
|
|
2014-01-07 23:41:55 +01:00
|
|
|
channel.post({
|
2016-04-03 07:58:06 +02:00
|
|
|
url: '/json/users/me/presence',
|
2019-06-29 02:55:26 +02:00
|
|
|
data: {
|
2019-03-29 00:13:07 +01:00
|
|
|
status: exports.compute_active_status(),
|
2019-06-29 02:55:26 +02:00
|
|
|
ping_only: !want_redraw,
|
|
|
|
new_user_input: exports.new_user_input,
|
2020-02-02 17:29:05 +01:00
|
|
|
slim_presence: true,
|
2019-06-29 02:55:26 +02:00
|
|
|
},
|
2014-01-07 23:41:55 +01:00
|
|
|
idempotent: true,
|
|
|
|
success: function (data) {
|
|
|
|
// Update Zephyr mirror activity warning
|
|
|
|
if (data.zephyr_mirror_active === false) {
|
2017-04-05 23:33:14 +02:00
|
|
|
$('#zephyr-mirror-error').addClass("show");
|
2014-01-07 23:41:55 +01:00
|
|
|
} else {
|
2017-04-05 23:33:14 +02:00
|
|
|
$('#zephyr-mirror-error').removeClass("show");
|
2014-01-07 23:41:55 +01:00
|
|
|
}
|
2013-06-12 19:58:25 +02:00
|
|
|
|
2014-01-07 23:41:55 +01:00
|
|
|
exports.new_user_input = false;
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2017-03-06 15:24:43 +01:00
|
|
|
if (want_redraw) {
|
2017-03-30 20:04:01 +02:00
|
|
|
presence.set_info(data.presences, data.server_timestamp);
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.redraw();
|
2017-03-06 15:24:43 +01:00
|
|
|
}
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2013-02-07 19:57:45 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-06-29 02:50:32 +02:00
|
|
|
function mark_client_active() {
|
2019-06-29 02:52:15 +02:00
|
|
|
if (!exports.client_is_active) {
|
|
|
|
exports.client_is_active = true;
|
2019-06-29 02:53:22 +02:00
|
|
|
send_presence_to_server(false);
|
2013-02-07 19:57:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.initialize = function () {
|
2017-05-24 22:32:17 +02:00
|
|
|
$("html").on("mousemove", function () {
|
|
|
|
exports.new_user_input = true;
|
|
|
|
});
|
|
|
|
|
2019-06-29 02:50:32 +02:00
|
|
|
$(window).focus(mark_client_active);
|
2013-02-12 20:40:28 +01:00
|
|
|
$(window).idle({idle: DEFAULT_IDLE_TIMEOUT_MS,
|
2019-06-29 02:50:32 +02:00
|
|
|
onIdle: mark_client_idle,
|
|
|
|
onActive: mark_client_active,
|
2018-05-06 21:43:17 +02:00
|
|
|
keepTracking: true});
|
2013-02-07 19:57:45 +01:00
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.set_cursor_and_filter();
|
2017-10-07 18:57:44 +02:00
|
|
|
|
2017-02-21 02:10:54 +01:00
|
|
|
exports.build_user_sidebar();
|
|
|
|
exports.update_huddles();
|
2017-03-06 15:24:43 +01:00
|
|
|
|
2018-07-16 15:16:33 +02:00
|
|
|
buddy_list.start_scroll_handler();
|
|
|
|
|
2017-03-06 15:24:43 +01:00
|
|
|
// Let the server know we're here, but pass "false" for
|
|
|
|
// want_redraw, since we just got all this info in page_params.
|
2019-06-29 02:53:22 +02:00
|
|
|
send_presence_to_server(false);
|
2017-03-06 15:24:43 +01:00
|
|
|
|
|
|
|
function get_full_presence_list_update() {
|
2019-06-29 02:53:22 +02:00
|
|
|
send_presence_to_server(true);
|
2017-03-06 15:24:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
setInterval(get_full_presence_list_update, ACTIVE_PING_INTERVAL_MS);
|
2013-04-03 22:00:02 +02:00
|
|
|
};
|
|
|
|
|
2020-02-03 16:25:13 +01:00
|
|
|
exports.update_presence_info = function (user_id, info, server_time) {
|
2020-02-07 17:19:03 +01:00
|
|
|
presence.update_info_from_event(user_id, info, server_time);
|
2019-01-25 19:20:51 +01:00
|
|
|
exports.redraw_user(user_id);
|
2013-10-18 00:56:49 +02:00
|
|
|
exports.update_huddles();
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2013-02-07 19:57:45 +01:00
|
|
|
};
|
|
|
|
|
2018-12-19 18:41:47 +01:00
|
|
|
exports.on_set_away = function (user_id) {
|
|
|
|
user_status.set_away(user_id);
|
2019-01-25 19:20:51 +01:00
|
|
|
exports.redraw_user(user_id);
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2018-12-19 18:41:47 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.on_revoke_away = function (user_id) {
|
|
|
|
user_status.revoke_away(user_id);
|
2019-01-25 19:20:51 +01:00
|
|
|
exports.redraw_user(user_id);
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2018-12-19 18:41:47 +01:00
|
|
|
};
|
|
|
|
|
2017-01-04 23:54:59 +01:00
|
|
|
exports.redraw = function () {
|
2017-02-21 00:53:08 +01:00
|
|
|
exports.build_user_sidebar();
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.user_cursor.redraw();
|
2017-01-04 23:54:59 +01:00
|
|
|
exports.update_huddles();
|
2019-02-18 16:32:27 +01:00
|
|
|
pm_list.update_private_messages();
|
2017-01-04 23:54:59 +01:00
|
|
|
};
|
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.reset_users = function () {
|
|
|
|
// Call this when we're leaving the search widget.
|
|
|
|
exports.build_user_sidebar();
|
|
|
|
exports.user_cursor.clear();
|
2014-01-14 17:57:34 +01:00
|
|
|
};
|
|
|
|
|
2018-04-19 23:14:58 +02:00
|
|
|
exports.narrow_for_user = function (opts) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_id = buddy_list.get_key_from_li({li: opts.li});
|
2018-04-21 14:59:03 +02:00
|
|
|
return exports.narrow_for_user_id({user_id: user_id});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.narrow_for_user_id = function (opts) {
|
2020-02-05 14:30:59 +01:00
|
|
|
const person = people.get_by_user_id(opts.user_id);
|
2019-11-02 00:06:25 +01:00
|
|
|
const email = person.email;
|
2018-04-19 23:14:58 +02:00
|
|
|
|
2018-04-23 06:02:11 +02:00
|
|
|
narrow.by('pm-with', email, {trigger: 'sidebar'});
|
2018-04-19 17:47:41 +02:00
|
|
|
exports.user_filter.clear_and_hide_search();
|
2018-04-19 23:14:58 +02:00
|
|
|
};
|
|
|
|
|
2018-02-12 23:31:17 +01:00
|
|
|
function keydown_enter_key() {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_id = exports.user_cursor.get_key();
|
2018-04-21 14:59:03 +02:00
|
|
|
if (user_id === undefined) {
|
|
|
|
return;
|
2014-02-13 23:08:41 +01:00
|
|
|
}
|
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.narrow_for_user_id({user_id: user_id});
|
|
|
|
popovers.hide_all();
|
2018-02-12 23:31:17 +01:00
|
|
|
}
|
|
|
|
|
2018-04-21 14:59:03 +02:00
|
|
|
exports.set_cursor_and_filter = function () {
|
|
|
|
exports.user_cursor = list_cursor({
|
|
|
|
list: buddy_list,
|
|
|
|
highlight_class: 'highlighted_user',
|
|
|
|
});
|
2018-02-12 23:31:17 +01:00
|
|
|
|
2018-04-19 17:47:41 +02:00
|
|
|
exports.user_filter = user_search({
|
|
|
|
update_list: update_users_for_search,
|
2018-04-21 14:59:03 +02:00
|
|
|
reset_items: exports.reset_users,
|
|
|
|
on_focus: exports.user_cursor.reset,
|
2018-04-19 17:47:41 +02:00
|
|
|
});
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const $input = exports.user_filter.input_field();
|
2018-04-21 14:59:03 +02:00
|
|
|
|
|
|
|
$input.on('blur', exports.user_cursor.clear);
|
|
|
|
|
|
|
|
keydown_util.handle({
|
|
|
|
elem: $input,
|
|
|
|
handlers: {
|
|
|
|
enter_key: function () {
|
|
|
|
keydown_enter_key();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
up_arrow: function () {
|
|
|
|
exports.user_cursor.prev();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
down_arrow: function () {
|
|
|
|
exports.user_cursor.next();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2017-10-05 06:06:42 +02:00
|
|
|
};
|
2014-01-13 19:34:24 +01:00
|
|
|
|
2018-04-19 17:47:41 +02:00
|
|
|
exports.initiate_search = function () {
|
|
|
|
if (exports.user_filter) {
|
|
|
|
exports.user_filter.initiate_search();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.escape_search = function () {
|
|
|
|
if (exports.user_filter) {
|
|
|
|
exports.user_filter.escape_search();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-10-07 19:55:44 +02:00
|
|
|
exports.get_filter_text = function () {
|
2018-04-19 17:47:41 +02:00
|
|
|
if (!exports.user_filter) {
|
2017-10-07 19:55:44 +02:00
|
|
|
// This may be overly defensive, but there may be
|
|
|
|
// situations where get called before everything is
|
2017-11-09 16:26:38 +01:00
|
|
|
// fully initialized. The empty string is a fine
|
2017-10-07 19:55:44 +02:00
|
|
|
// default here.
|
|
|
|
blueslip.warn('get_filter_text() is called before initialization');
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2018-04-19 17:47:41 +02:00
|
|
|
return exports.user_filter.text();
|
2017-10-07 19:55:44 +02:00
|
|
|
};
|
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
window.activity = exports;
|