Commit Graph

148 Commits

Author SHA1 Message Date
Anders Kaseorg a43307bc4e js: Use Moment.js as a module.
Note that require("moment") and require("moment-timezone") resolve to
the same thing, but the latter adds timezone support as a side effect.
So I went with the latter in every file where .tz is used.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-28 10:54:35 -07:00
Anders Kaseorg f0021df3d4 people: Use blueimp-md5 as a module.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-28 10:54:35 -07:00
Anders Kaseorg d2520cd7e0 js: Replace underscore with lodash and remove it from globals.
Tweaked by tabbott to bump PROVISION_VERSION.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-26 16:12:06 -07:00
Anders Kaseorg 96dcc0ce6e js: Use ES6 object literal shorthand syntax.
Generated by ESLint.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-21 12:42:22 -07:00
Anders Kaseorg b65d2e063d js: Reformat with Prettier.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-17 14:31:25 -07:00
Anders Kaseorg 883e2fd325 js: Remove inner spacing from object literals.
We’re configuring Prettier with bracketSpacing: false.  Generated by
ESLint.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-17 14:31:25 -07:00
Anders Kaseorg f3726db89a js: Normalize strings to double quotes.
Prettier would do this anyway, but it’s separated out for a more
reviewable diff.  Generated by ESLint.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-17 14:31:24 -07:00
Anders Kaseorg e014ea966a eslint: Enable comma-dangle for functions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-03 16:55:51 -07:00
Anders Kaseorg a79322bc94 eslint: Enable prefer-arrow-callback.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-03 16:55:50 -07:00
Anders Kaseorg 615b7fcc2c eslint: Enable arrow-body-style.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-03 16:55:50 -07:00
Anders Kaseorg b0253c5a2e eslint: Enable arrow-parens.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-03 16:53:39 -07:00
Steve Howell ad8e79a22e urls: Support simple user_id-based slugs. 2020-07-03 16:50:18 -07:00
Hashir Sarwar 9b8521faee people: Enable `user_avatar_url_field_optional` in webapp.
In 5200598a31, we introduced a new
client capability that can be used to avoid unreasonable network
bandwidth consumed sending avatar URLs of long term idle users in
organizations with 10,000s members.

This commit enables this feature and adds support for it to the web
client.
2020-06-24 10:06:35 -07:00
sahil839 c7a68d60f3 realm owner: Add frontend implementation of owner role.
This commit adds the option of owner role in user role dropdown
and also takes care of the restrictions while adding/removing
owner status of the user.
This commit also handles the places where we dispaly role of
the user in UI.
2020-06-14 21:20:30 -07:00
Aman Agrawal bc7136590a recent_topics: Add avatars of recent senders to topic. 2020-06-09 22:08:31 -07:00
sahil839 bd943941e4 people: Remove 'get_active_user_for_email' function.
This commit removes the 'get_active_user_for_email' function
from people.js. We have removed the use of this function
in the previous commits, which changed the functions using
'get_active_user_for_email' to use user_ids instead of emails.
2020-06-05 16:08:26 -07:00
Clara Dantas e1e755c887 people.js: Change functions to return a list of ids instead of objects.
The get_active_humans and get_non_active_humans functions used
to return a list of user objects. The get_active_humans is used
on settings_users.js and settings_bots.js, and in both places the
only attributes needed of the person object are the user_id and
full_name.

To make the function return smaller, instead of a list of active
humans, we are returning a list of active human ids, saving memory.
With the ids we can call the people API to get the full_name attribute.
2020-06-04 14:23:52 -04:00
clarammdantas de00c3cd6a people.js: Add non_active_user_dict bucket. 2020-05-26 21:41:54 -07:00
clarammdantas aae7c79c00 people.js: Rename add() to add_active_user(). 2020-05-26 21:41:54 -07:00
clarammdantas c90c8c0b19 people.js: Create new add_cross_realm_user function.
The people.js tests were using _add_user function to add
cross realm bots. The problem is that _add_user function
doesn't properly simulates the adding process as it doesn't
add the user in cross_realm_dict as well.

To solve this and eliminate the need of calling
people.initialize(), which means the params obj needs to be
defined, we extracted the whole logic of adding a cross realm
user into a separete function, add_cross_realm_user.
2020-05-26 21:41:54 -07:00
Steve Howell ede709f75c bug fix: Fix sorting for group-pm edge cases.
If you have a group PM where some users have
three-digit user_ids and some with four-digit
user_ids (or similar), a huddle could effectively
be ignored when determining the order of
search search suggestions.

Basically, we need a way to canonically sort
user_ids in "huddle" strings, and it's somewhat
arbitrary whether you sort lexically or sort
numerically, but you do need to be consistent
about it.

And JS is not exactly helpful here:

    > [99, 101].sort()
    [ 101, 99 ]

This is a pretty obscure bug with pretty low
user-facing consequences, and it was never
reported to us as far as I know, but the fix
here is pretty straightforward.

We have had similar bugs of slightly more consequence
in the past.  The reason this bug has shown
up multiple times in our codebase is that every
component that deals with huddles has slightly
different forces that determine how it wants
to serialize the huddle.  It's just one of those
annoying things.  Plus, bugs with group PMs
do tend to escape detection, since most people
spend most of their time either on streams
or in 1:1 PMs.
2020-05-26 21:26:11 -07:00
Steve Howell 4803a12416 search: Extract people.huddle_concat().
This is a pure code extraction.  The current
code is buggy with respect to user_ids with
different lengths of digits, i.e. it does
a naive lexical sort instead of a numerical
sort.  We'll fix that in the next commit.
2020-05-26 21:26:11 -07:00
Steve Howell a3e265f5b8 people tests: Move test users to top of file.
I consolidate most of our users toward the top
of the file, so that we don't have to clutter
up individual tests.  This also avoids some
confusion where charles/maria got repeated
in different tests with different ids.

I also introduce a couple four-digit ids to
try to expose more bugs related to sorting.

Note that it's still easy to keep tests
isolated here, as we have always been able
to cheaply re-initialize `people.js` and then
add individual users back.

There are still some tests where it makes
sense to just declare users locally, especially
if we are mutating their data.

There are a few minor incidental cleanups here,
mostly involving replacing hard coded ids
with things like `maria.user_id`.
2020-05-26 17:21:43 -04:00
sahil839 0b9bea4566 typeahead: Show only active users in mention typeaheads.
'get_active_message_people` function is added which returns active
users who have sent the messages that are currently showing up in
the feed.

typeahead fetches the users from 'get_active_message_people` instead
of `get_message_people` and thus shows only active users in the
mention typeahead and excludes deactivated users.

Fixes #14310
2020-04-22 16:50:24 -07:00
sahil839 6657cddd7a refractor: Move get_visible_email from popovers.js to people.js.
This commit moves the get_visible_email function to people.js
as this function will be used in other places and people.js seems
relevant file for this.

Tests are added to get full coverage.
2020-04-20 20:48:47 -07:00
Rohitt Vashishtha c8b0627ffe zblueslip: Run blueslip.reset after each test.
This simplifies the blueslip interface to just declaring expected
errors and calling the code to test.
2020-04-20 08:17:20 -04:00
Steve Howell e64059de79 node tests: Remove most test_log length assertions.
For all the places where we just make zero or one
blueslip call, asserting for length is either
unnecessary or overkill.
2020-04-08 11:37:27 -04:00
Steve Howell f7b432afec node tests: Auto-include zblueslip for node tests.
We already use blueslip stubs in ~45 tests, so we
may as well just auto-include it.
2020-04-03 12:56:49 -04:00
Steve Howell df84c52a7f zblueslip: Change API to expect/reset.
The `set_test_data` never made complete sense to
me, since it wasn't really data that we were
setting.
2020-04-03 12:56:49 -04:00
Stefan Weil d2fa058cc1
text: Fix some typos (most of them found and fixed by codespell).
Signed-off-by: Stefan Weil <sw@weilnetz.de>
2020-03-27 17:25:56 -07:00
Steve Howell 58b72a2194 refactor: Make bot owner hovers more robust.
This is code simplification motivated
by a recent bug that we fixed with some
server changes, but which was really
caused in some sense by our client code
using an overly finicky
condition to check falsiness.

For cross-realm bots, the value of
`user.bot_owner_id` may be `null`, or it
may simply be `undefined`, depending
on whether the server passes `None`
or simply omits the field.

We don't want out client code to be
coupled to that rather arbitrary
decision.

We were doing a `!== null` check instead
of checking for falsiness, which led to
blueslip errors in the past.  Because a
bot owner id could be plausibly 0, a falsiness
check would be brittle in a different way.

Now we avoid that ugliness by calling
`get_bot_owner_user`, which either returns
an object or `undefined`.

And then the caller can just do a concise
check for whether `bot_owner` exists.

And we also fix up the crufty code that
was putting `bot_owner_full_name` on to
the object instead of using a local.

We have a bug report for this again, although
it might be on an old branch.

Fixes #13621.
2020-03-24 20:40:19 -07:00
Steve Howell f0c99b42ec Rename people.add_in_realm to people.add().
We had this API:

    people.add_in_realm = full-fledged user
    people.add = not necessarily in realm

Now the API is this:

    people.add = full-fledged user
    people._add_user = internal API for cross-realm bots
        and deactivated users

I think in most of our tests the distinction between
people.add() and people.add_in_realm() was just an
accident of history and didn't reflect any real intention.

And if I had to guess the intention in 99% of the cases,
folks probably thought they were just creating ordinary,
active users in the current realm.

In places where the distinction was obviously important
(because a test failed), I deactivated the user via
`people.deactivate`.

For the 'basics' test in the people test suite, I clean
up the test setup for Isaac.  Before this commit I was
adding him first as a non-realm user then as a full-fledged
user, but this was contrived and confusing, and we
didn't really need it for test coverage purposes.
2020-03-22 10:55:11 -07:00
Steve Howell 25d2e2e122 Rename get_realm_persons() to get_realm_users().
The function's name was misleading, since it includes
any bots in your realm.
2020-03-22 10:55:11 -07:00
Steve Howell 7ac5d0602b minor: Rename function to get_active_humans().
Saying `human_persons` is a bit redundant (although
kind of an artifact of our legacy use of `person`
when we really mean `user`.)
2020-03-22 10:55:11 -07:00
Steve Howell 7088d09094 presence/people: Use people.get_active_human_count().
Before this commit, presence used get_realm_count()
to determine whether a realm was "small" (and thus
should show all human users in the buddy list, even
humans that had not been active in a while).

The `get_realm_count` function--despite a very wrong,
misleading comment--was including bots in its count.

The new function truly counts only active humans
(and no bots).

Because we were overcounting users before this change,
we should technically adjust `BIG_REALM_COUNT` down
by some amount to reflect our original intention there
on the parameter.  I'm leaving it alone for now, though,
since we've improved the performance of the buddy list
over time, and it's probably fine if a few "big" realms
get re-classified as small realms (and show more users)
by virtue of this change.

(Also note that this cutoff value only affects the
"normal" view of the buddy list; both small realms
and large realms will show long-inactive users if you
do searches.)

Fixes #14215
2020-03-22 10:55:11 -07:00
Steve Howell 979dcfe85b refactor: Extract settings_data.py.
This extracts a new module with three
functions, which we will test with 100%
line coverage:

    - show_email
    - email_for_user_settings
    - get_time_preferences

The first two break several dependencies
in the codebase on `settings_org.js`.  The
`get_time_preferences` breaks an annoying
dependency on `page_params` within people.

The module is pretty cohesive, in terms that
all three functions are just light wrappers
around `page_params` and/or `settings_config`.

Now all the modules that want to call show_email()
only have to require `settings_data`, instead of
having a dependency on the much heavier
`settings_org.js` module.

I also make some of the unit tests here be more
full-stack, where instead of stubbing show_email,
I basically just toggle `page_params.is_admin`.
2020-02-28 17:11:24 -08:00
Steve Howell b994889315 node tests: Just set i18n every time.
Explicitly stubbing i18n in 48 different files
is mostly busy work at this point, and it doesn't
provide much signal, since often it's invoked
only to satisfy transitive dependencies.
2020-02-28 17:11:24 -08:00
Steve Howell da79fd206a ui_init: Handle page_params more cleanly.
This cleans up the handoff of page_params
data between ui_init and modules that
take over ownership of page_params-derived
data.

Read the long comment in ui_init for a bit
more context.

Most of this diff is actually test cleanup.
And a lot of the diff to "real" code is
just glorified `s/page_params/params/`
in the `initialize` functions.

One little oddity is that we don't actually
surrender ownership of `page_params.user_id`
to `people.js`.  We could plausibly sweep
the rest of the codebase to just use
`people.my_user_id()` consistently, but it's
not a super high priority thing to fix,
since the value never changes.

The stream_data situation is a bit messy,
since we consume `page_params` data in the
initialize() function in addition to the
`params` data we "own".  I added a comment
there and intend to follow up.  I tried
to mostly avoid the "word soup" by extracting
three locals at the top.

Finally, I don't touch `alert_words` yet,
despite it also doing the delete-page-params-data
dance.  The problem is that `alert_words`
doesn't have a proper `initialize()`.  We
should clean that up and have it use a
`Map` internally, too.
2020-02-26 13:14:09 -08:00
Steve Howell d2a62d2055 node tests: Fix dead code in people tests. 2020-02-26 07:19:00 -05:00
Steve Howell e8de4abb0e markdown: Clean up userMentionHandler().
This mostly moves logic into people.js.
The people functions added here are glorified
two-liners.

One thing that changes here is that we
are a bit more rigorous about duplicate
names.

The code is slightly awkward, because this
commit preserves the strange behavior
that if 'alice|42' doesn't match on
the user with the name "alice" and user_id
"42", we instead look for a user whose
name is "alice|42".  That seems like a
misfeature to me, but there's a test for
it, so I want to check with Tim that it's not
intentional behavior before I simplify
the code.
2020-02-18 16:04:12 -08:00
Steve Howell 9ab07d1038 util.js: Remove util from window.
We now treat util like a leaf module and
use "require" to import it everywhere it's used.

An earlier version of this commit moved
util into our "shared" library, but we
decided to wait on that.  Once we're ready
to do that, we should only need to do a
simple search/replace on various
require/zrequire statements plus a small
tweak to one of the custom linter checks.

It turns out we don't really need util.js
for our most immediate code-sharing goal,
which is to reuse our markdown code on
mobile.  There's a little bit of cleanup
still remaining to break the dependency,
but it's minor.

The util module still calls the global
blueslip module in one place, but that
code is about to be removed in the next
few commits.

I am pretty confident that once we start
sharing things like the typeahead code
more aggressively, we'll start having
dependencies on util.  The module is barely
more than 300 lines long, so we'll probably
just move the whole thing into shared
rather than break it apart.  Also, we
can continue to nibble away at the
cruftier parts of the module.
2020-02-15 12:20:20 -08:00
Anders Kaseorg 050915c46c js: Convert _.size(a) to a.length.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-10 14:08:12 -08:00
Anders Kaseorg 4948240619 js: Convert _.filter(a, …) to a.filter(…).
And convert the corresponding function expressions to arrow style
while we’re here.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-10 14:08:12 -08:00
Steve Howell e9c6653852 node tests: Always enforce blueslip warn/error/fatal.
We now require all of our unit tests to handle
blueslip errors for warn/error/fatal.  This
simplifies the zblueslip code to not have any
options passed in.

Most of the places changed here fell into two
categories:

    - We were just missing a random piece of
      setup data in a happy path test.

    - We were testing error handling in just
      a lazy way to ensure 100% coverage.  Often
      these error codepaths were fairly
      contrived.

The one place where we especially lazy was
the stream_data tests, and those are now
more thorough.
2020-02-07 14:15:44 -08:00
Anders Kaseorg 02511bff1c 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-07 14:09:47 -08:00
Steve Howell b8f01f9cda people: Rename method to get_by_user_id().
This name is consistent with:

    get_by_email()
    get_by_name()
2020-02-05 12:04:56 -08:00
Anders Kaseorg 60fac80c8e dict, lazy_set: Replace num_items method with size property.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-04 12:22:03 -08:00
Steve Howell 890a4b1247 refactor: Add filterer for user settings.
This change sets us up to optimize how we
filter users in the admin user settings.
See #13554 for more context on the user
facing issues.

This fix is basically three related things:

    - Add filterer options to list_render.
    - Add helper method to people.js.
    - Use filterer in settings_users.js.
2020-01-14 22:43:08 -08:00
Steve Howell bc5589c2a7 people: Clean up recip.id code.
When we are pulling data from message.display_recipient
for private messages, the user_id field is always
called 'id', not 'user_id', so we can simplify
some defensive code.
2020-01-05 12:27:30 -08:00
Steve Howell 26168eaa98 search: Optimize search bar suggestions for large realms.
We only ever show 3 or 4 people in search suggestions
(possibly w/a couple variations, like pm-with/sender/etc.),
so we can try to search a smaller subset of people
before going through the entire realm.

We use message_store.user_ids() for this, since you
typically want to search messages for people that
have sent messages recently, and we already sort
based on PM conversations.
2020-01-04 12:58:00 -08:00