mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	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>
		
	
		
			
				
	
	
		
			495 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			495 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const render_group_pms = require('../templates/group_pms.hbs');
 | 
						|
const Dict = require('./dict').Dict;
 | 
						|
 | 
						|
/*
 | 
						|
    Helpers for detecting user activity and managing user idle states
 | 
						|
*/
 | 
						|
 | 
						|
/* Broadcast "idle" to server after 5 minutes of local inactivity */
 | 
						|
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
 | 
						|
/* Time between keep-alive pings */
 | 
						|
const ACTIVE_PING_INTERVAL_MS = 50 * 1000;
 | 
						|
 | 
						|
/* Keep in sync with views.py:update_active_status_backend() */
 | 
						|
exports.ACTIVE = "active";
 | 
						|
exports.IDLE = "idle";
 | 
						|
 | 
						|
// 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.
 | 
						|
exports.new_user_input = true;
 | 
						|
 | 
						|
const huddle_timestamps = new Dict();
 | 
						|
 | 
						|
function update_pm_count_in_dom(count_span, value_span, count) {
 | 
						|
    const li = count_span.parents('li');
 | 
						|
 | 
						|
    if (count === 0) {
 | 
						|
        count_span.hide();
 | 
						|
        li.removeClass("user-with-count");
 | 
						|
        value_span.text('');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    count_span.show();
 | 
						|
    li.addClass("user-with-count");
 | 
						|
    value_span.text(count);
 | 
						|
}
 | 
						|
 | 
						|
function update_group_count_in_dom(count_span, value_span, count) {
 | 
						|
    const li = count_span.parent();
 | 
						|
 | 
						|
    if (count === 0) {
 | 
						|
        count_span.hide();
 | 
						|
        li.removeClass("group-with-count");
 | 
						|
        value_span.text('');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    count_span.show();
 | 
						|
    li.addClass("group-with-count");
 | 
						|
    value_span.text(count);
 | 
						|
}
 | 
						|
 | 
						|
function get_pm_list_item(user_id) {
 | 
						|
    return buddy_list.find_li({
 | 
						|
        key: user_id,
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function get_group_list_item(user_ids_string) {
 | 
						|
    return $("li.group-pms-sidebar-entry[data-user-ids='" + user_ids_string + "']");
 | 
						|
}
 | 
						|
 | 
						|
function set_pm_count(user_ids_string, count) {
 | 
						|
    const count_span = get_pm_list_item(user_ids_string).find('.count');
 | 
						|
    const value_span = count_span.find('.value');
 | 
						|
    update_pm_count_in_dom(count_span, value_span, count);
 | 
						|
}
 | 
						|
 | 
						|
function set_group_count(user_ids_string, count) {
 | 
						|
    const count_span = get_group_list_item(user_ids_string).find('.count');
 | 
						|
    const value_span = count_span.find('.value');
 | 
						|
    update_group_count_in_dom(count_span, value_span, count);
 | 
						|
}
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
    for (const [user_ids_string, count] of counts.pm_count) {
 | 
						|
        // TODO: just use user_ids_string in our markup
 | 
						|
        const is_pm = !user_ids_string.includes(',');
 | 
						|
        if (is_pm) {
 | 
						|
            set_pm_count(user_ids_string, count);
 | 
						|
        } else {
 | 
						|
            set_group_count(user_ids_string, count);
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
exports.process_loaded_messages = function (messages) {
 | 
						|
    let need_resize = false;
 | 
						|
 | 
						|
    for (const message of messages) {
 | 
						|
        const huddle_string = people.huddle_string(message);
 | 
						|
 | 
						|
        if (huddle_string) {
 | 
						|
            const old_timestamp = huddle_timestamps.get(huddle_string);
 | 
						|
 | 
						|
            if (!old_timestamp || old_timestamp < message.timestamp) {
 | 
						|
                huddle_timestamps.set(huddle_string, message.timestamp);
 | 
						|
                need_resize = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    exports.update_huddles();
 | 
						|
 | 
						|
    if (need_resize) {
 | 
						|
        resize.resize_page_components(); // big hammer
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
exports.get_huddles = function () {
 | 
						|
    let huddles = Array.from(huddle_timestamps.keys());
 | 
						|
    huddles = _.sortBy(huddles, function (huddle) {
 | 
						|
        return huddle_timestamps.get(huddle);
 | 
						|
    });
 | 
						|
    return huddles.reverse();
 | 
						|
};
 | 
						|
 | 
						|
function huddle_split(huddle) {
 | 
						|
    return huddle.split(',').map(s => parseInt(s, 10));
 | 
						|
}
 | 
						|
 | 
						|
exports.full_huddle_name = function (huddle) {
 | 
						|
    const user_ids = huddle_split(huddle);
 | 
						|
 | 
						|
    const names = user_ids.map(user_id => {
 | 
						|
        const person = people.get_by_user_id(user_id);
 | 
						|
        return person.full_name;
 | 
						|
    });
 | 
						|
 | 
						|
    return names.join(', ');
 | 
						|
};
 | 
						|
 | 
						|
exports.short_huddle_name = function (huddle) {
 | 
						|
    const user_ids = huddle_split(huddle);
 | 
						|
 | 
						|
    const num_to_show = 3;
 | 
						|
    let names = user_ids.map(user_id => {
 | 
						|
        const person = people.get_by_user_id(user_id);
 | 
						|
        return person.full_name;
 | 
						|
    });
 | 
						|
 | 
						|
    names = _.sortBy(names, function (name) { return name.toLowerCase(); });
 | 
						|
    names = names.slice(0, num_to_show);
 | 
						|
    const others = user_ids.length - num_to_show;
 | 
						|
 | 
						|
    if (others === 1) {
 | 
						|
        names.push("+ 1 other");
 | 
						|
    } else if (others >= 2) {
 | 
						|
        names.push("+ " + others + " others");
 | 
						|
    }
 | 
						|
 | 
						|
    return names.join(', ');
 | 
						|
};
 | 
						|
 | 
						|
function mark_client_idle() {
 | 
						|
    // 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.
 | 
						|
    exports.client_is_active = false;
 | 
						|
}
 | 
						|
 | 
						|
exports.redraw_user = function (user_id) {
 | 
						|
    if (page_params.realm_presence_disabled) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const filter_text = exports.get_filter_text();
 | 
						|
 | 
						|
    if (!buddy_data.matches_filter(filter_text, user_id)) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const info = buddy_data.get_item(user_id);
 | 
						|
 | 
						|
    buddy_list.insert_or_move({
 | 
						|
        key: user_id,
 | 
						|
        item: info,
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.searching = function () {
 | 
						|
    return exports.user_filter && exports.user_filter.searching();
 | 
						|
};
 | 
						|
 | 
						|
exports.build_user_sidebar = function () {
 | 
						|
    if (page_params.realm_presence_disabled) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const filter_text = exports.get_filter_text();
 | 
						|
 | 
						|
    const user_ids = buddy_data.get_filtered_and_sorted_user_ids(filter_text);
 | 
						|
 | 
						|
    const finish = blueslip.start_timing('buddy_list.populate');
 | 
						|
    buddy_list.populate({
 | 
						|
        keys: user_ids,
 | 
						|
    });
 | 
						|
    finish();
 | 
						|
 | 
						|
    resize.resize_page_components();
 | 
						|
 | 
						|
    return user_ids; // for testing
 | 
						|
};
 | 
						|
 | 
						|
function do_update_users_for_search() {
 | 
						|
    // Hide all the popovers but not userlist sidebar
 | 
						|
    // when the user is searching.
 | 
						|
    popovers.hide_all_except_sidebars();
 | 
						|
    exports.build_user_sidebar();
 | 
						|
    exports.user_cursor.reset();
 | 
						|
}
 | 
						|
 | 
						|
const update_users_for_search = _.throttle(do_update_users_for_search, 50);
 | 
						|
 | 
						|
function show_huddles() {
 | 
						|
    $('#group-pm-list').addClass("show");
 | 
						|
}
 | 
						|
 | 
						|
function hide_huddles() {
 | 
						|
    $('#group-pm-list').removeClass("show");
 | 
						|
}
 | 
						|
 | 
						|
exports.update_huddles = function () {
 | 
						|
    if (page_params.realm_presence_disabled) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    const huddles = exports.get_huddles().slice(0, 10);
 | 
						|
 | 
						|
    if (huddles.length === 0) {
 | 
						|
        hide_huddles();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    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),
 | 
						|
    }));
 | 
						|
 | 
						|
    const html = render_group_pms({group_pms: group_pms});
 | 
						|
    ui.get_content_element($('#group-pms')).html(html);
 | 
						|
 | 
						|
    for (const user_ids_string of huddles) {
 | 
						|
        const count = unread.num_unread_for_person(user_ids_string);
 | 
						|
        set_group_count(user_ids_string, count);
 | 
						|
    }
 | 
						|
 | 
						|
    show_huddles();
 | 
						|
};
 | 
						|
 | 
						|
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;
 | 
						|
};
 | 
						|
 | 
						|
function send_presence_to_server(want_redraw) {
 | 
						|
    if (reload_state.is_in_progress()) {
 | 
						|
        blueslip.log("Skipping querying presence because reload in progress");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    channel.post({
 | 
						|
        url: '/json/users/me/presence',
 | 
						|
        data: {
 | 
						|
            status: exports.compute_active_status(),
 | 
						|
            ping_only: !want_redraw,
 | 
						|
            new_user_input: exports.new_user_input,
 | 
						|
            slim_presence: true,
 | 
						|
        },
 | 
						|
        idempotent: true,
 | 
						|
        success: function (data) {
 | 
						|
 | 
						|
            // Update Zephyr mirror activity warning
 | 
						|
            if (data.zephyr_mirror_active === false) {
 | 
						|
                $('#zephyr-mirror-error').addClass("show");
 | 
						|
            } else {
 | 
						|
                $('#zephyr-mirror-error').removeClass("show");
 | 
						|
            }
 | 
						|
 | 
						|
            exports.new_user_input = false;
 | 
						|
 | 
						|
            // Zulip has 2 data feeds coming from the server to the
 | 
						|
            // client: The server_events data, and this presence feed.
 | 
						|
            // Everything in 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 throwing
 | 
						|
            // exceptions due to users appearing in presence that we
 | 
						|
            // haven't learned about yet.  We handle this in 2 stages.
 | 
						|
            // First, here, we make sure that we've confirmed whether
 | 
						|
            // we are indeed in the unsuspend case.  Then, in
 | 
						|
            // `presence.set_info`, we only complain about unknown
 | 
						|
            // users if server_events does not suspect we're offline.
 | 
						|
            server_events.check_for_unsuspend();
 | 
						|
 | 
						|
            if (want_redraw) {
 | 
						|
                presence.set_info(data.presences, data.server_timestamp);
 | 
						|
                exports.redraw();
 | 
						|
            }
 | 
						|
        },
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function mark_client_active() {
 | 
						|
    if (!exports.client_is_active) {
 | 
						|
        exports.client_is_active = true;
 | 
						|
        send_presence_to_server(false);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
exports.initialize = function () {
 | 
						|
    $("html").on("mousemove", function () {
 | 
						|
        exports.new_user_input = true;
 | 
						|
    });
 | 
						|
 | 
						|
    $(window).focus(mark_client_active);
 | 
						|
    $(window).idle({idle: DEFAULT_IDLE_TIMEOUT_MS,
 | 
						|
                    onIdle: mark_client_idle,
 | 
						|
                    onActive: mark_client_active,
 | 
						|
                    keepTracking: true});
 | 
						|
 | 
						|
    presence.set_info(page_params.presences,
 | 
						|
                      page_params.initial_servertime);
 | 
						|
    delete page_params.presences;
 | 
						|
 | 
						|
    exports.set_cursor_and_filter();
 | 
						|
 | 
						|
    exports.build_user_sidebar();
 | 
						|
    exports.update_huddles();
 | 
						|
 | 
						|
    buddy_list.start_scroll_handler();
 | 
						|
 | 
						|
    // Let the server know we're here, but pass "false" for
 | 
						|
    // want_redraw, since we just got all this info in page_params.
 | 
						|
    send_presence_to_server(false);
 | 
						|
 | 
						|
    function get_full_presence_list_update() {
 | 
						|
        send_presence_to_server(true);
 | 
						|
    }
 | 
						|
 | 
						|
    setInterval(get_full_presence_list_update, ACTIVE_PING_INTERVAL_MS);
 | 
						|
};
 | 
						|
 | 
						|
exports.update_presence_info = function (user_id, info, server_time) {
 | 
						|
    presence.set_info_for_user(user_id, info, server_time);
 | 
						|
    exports.redraw_user(user_id);
 | 
						|
    exports.update_huddles();
 | 
						|
    pm_list.update_private_messages();
 | 
						|
};
 | 
						|
 | 
						|
exports.on_set_away = function (user_id) {
 | 
						|
    user_status.set_away(user_id);
 | 
						|
    exports.redraw_user(user_id);
 | 
						|
    pm_list.update_private_messages();
 | 
						|
};
 | 
						|
 | 
						|
exports.on_revoke_away = function (user_id) {
 | 
						|
    user_status.revoke_away(user_id);
 | 
						|
    exports.redraw_user(user_id);
 | 
						|
    pm_list.update_private_messages();
 | 
						|
};
 | 
						|
 | 
						|
exports.redraw = function () {
 | 
						|
    exports.build_user_sidebar();
 | 
						|
    exports.user_cursor.redraw();
 | 
						|
    exports.update_huddles();
 | 
						|
    pm_list.update_private_messages();
 | 
						|
};
 | 
						|
 | 
						|
exports.reset_users = function () {
 | 
						|
    // Call this when we're leaving the search widget.
 | 
						|
    exports.build_user_sidebar();
 | 
						|
    exports.user_cursor.clear();
 | 
						|
};
 | 
						|
 | 
						|
exports.narrow_for_user = function (opts) {
 | 
						|
    const user_id = buddy_list.get_key_from_li({li: opts.li});
 | 
						|
    return exports.narrow_for_user_id({user_id: user_id});
 | 
						|
};
 | 
						|
 | 
						|
exports.narrow_for_user_id = function (opts) {
 | 
						|
    const person = people.get_by_user_id(opts.user_id);
 | 
						|
    const email = person.email;
 | 
						|
 | 
						|
    narrow.by('pm-with', email, {trigger: 'sidebar'});
 | 
						|
    exports.user_filter.clear_and_hide_search();
 | 
						|
};
 | 
						|
 | 
						|
function keydown_enter_key() {
 | 
						|
    const user_id = exports.user_cursor.get_key();
 | 
						|
    if (user_id === undefined) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    exports.narrow_for_user_id({user_id: user_id});
 | 
						|
    popovers.hide_all();
 | 
						|
}
 | 
						|
 | 
						|
exports.set_cursor_and_filter = function () {
 | 
						|
    exports.user_cursor = list_cursor({
 | 
						|
        list: buddy_list,
 | 
						|
        highlight_class: 'highlighted_user',
 | 
						|
    });
 | 
						|
 | 
						|
    exports.user_filter = user_search({
 | 
						|
        update_list: update_users_for_search,
 | 
						|
        reset_items: exports.reset_users,
 | 
						|
        on_focus: exports.user_cursor.reset,
 | 
						|
    });
 | 
						|
 | 
						|
    const $input = exports.user_filter.input_field();
 | 
						|
 | 
						|
    $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;
 | 
						|
            },
 | 
						|
        },
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
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();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
exports.get_filter_text = function () {
 | 
						|
    if (!exports.user_filter) {
 | 
						|
        // This may be overly defensive, but there may be
 | 
						|
        // situations where get called before everything is
 | 
						|
        // fully initialized.  The empty string is a fine
 | 
						|
        // default here.
 | 
						|
        blueslip.warn('get_filter_text() is called before initialization');
 | 
						|
        return '';
 | 
						|
    }
 | 
						|
 | 
						|
    return exports.user_filter.text();
 | 
						|
};
 | 
						|
 | 
						|
window.activity = exports;
 |