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.
This commit is contained in:
Steve Howell
2020-02-25 11:16:26 +00:00
committed by Tim Abbott
parent 6d211e359a
commit da79fd206a
16 changed files with 239 additions and 95 deletions

View File

@@ -2,15 +2,14 @@ const _settings_bots = {
render_bots: () => {}, render_bots: () => {},
}; };
const _page_params = { const bot_data_params = {
realm_bots: [{email: 'bot0@zulip.com', user_id: 42, full_name: 'Bot 0', realm_bots: [{email: 'bot0@zulip.com', user_id: 42, full_name: 'Bot 0',
services: []}, services: []},
{email: 'outgoingwebhook@zulip.com', user_id: 314, full_name: "Outgoing webhook", {email: 'outgoingwebhook@zulip.com', user_id: 314, full_name: "Outgoing webhook",
services: [{base_url: "http://foo.com", interface: 1}]}], services: [{base_url: "http://foo.com", interface: 1}]}],
is_admin: false,
}; };
set_global('page_params', _page_params); set_global('page_params', {is_admin: false});
set_global('settings_bots', _settings_bots); set_global('settings_bots', _settings_bots);
zrequire('people'); zrequire('people');
@@ -24,7 +23,7 @@ global.people.add({
global.people.initialize_current_user(42); global.people.initialize_current_user(42);
bot_data.initialize(); bot_data.initialize(bot_data_params);
// Our startup logic should have added Bot 0 from page_params. // Our startup logic should have added Bot 0 from page_params.
assert.equal(bot_data.get(42).full_name, 'Bot 0'); assert.equal(bot_data.get(42).full_name, 'Bot 0');
assert.equal(bot_data.get(314).full_name, 'Outgoing webhook'); assert.equal(bot_data.get(314).full_name, 'Outgoing webhook');

View File

@@ -276,11 +276,16 @@ run_test('get_invalid_recipient_emails', () => {
user_id: 124, user_id: 124,
full_name: 'Welcome Bot', full_name: 'Welcome Bot',
}; };
page_params.realm_users = [];
page_params.realm_non_active_users = [];
page_params.cross_realm_bots = [welcome_bot];
page_params.user_id = 30; page_params.user_id = 30;
people.initialize();
const params = {};
params.realm_users = [];
params.realm_non_active_users = [];
params.cross_realm_bots = [welcome_bot];
people.initialize(page_params.user_id, params);
compose_state.private_message_recipient('welcome-bot@example.com'); compose_state.private_message_recipient('welcome-bot@example.com');
assert.deepEqual(compose.get_invalid_recipient_emails(), []); assert.deepEqual(compose.get_invalid_recipient_emails(), []);
}); });

View File

@@ -91,7 +91,9 @@ run_test('process_from_server for differently rendered messages', () => {
run_test('build_display_recipient', () => { run_test('build_display_recipient', () => {
page_params.user_id = 123; page_params.user_id = 123;
page_params.realm_users = [
const params = {};
params.realm_users = [
{ {
user_id: 123, user_id: 123,
full_name: "Iago", full_name: "Iago",
@@ -103,9 +105,9 @@ run_test('build_display_recipient', () => {
user_id: 21, user_id: 21,
}, },
]; ];
page_params.realm_non_active_users = []; params.realm_non_active_users = [];
page_params.cross_realm_bots = []; params.cross_realm_bots = [];
people.initialize(); people.initialize(page_params.user_id, params);
let message = { let message = {
type: "stream", type: "stream",
@@ -160,16 +162,18 @@ run_test('insert_local_message', () => {
const local_id_float = 1; const local_id_float = 1;
page_params.user_id = 123; page_params.user_id = 123;
page_params.realm_users = [
const params = {};
params.realm_users = [
{ {
user_id: 123, user_id: 123,
full_name: "Iago", full_name: "Iago",
email: "iago@zulip.com", email: "iago@zulip.com",
}, },
]; ];
page_params.realm_non_active_users = []; params.realm_non_active_users = [];
page_params.cross_realm_bots = []; params.cross_realm_bots = [];
people.initialize(); people.initialize(page_params.user_id, params);
let apply_markdown_called = false; let apply_markdown_called = false;
let add_topic_links_called = false; let add_topic_links_called = false;

View File

@@ -877,7 +877,9 @@ run_test('track_duplicate_full_names', () => {
run_test('initialize', () => { run_test('initialize', () => {
people.init(); people.init();
global.page_params.realm_non_active_users = [ const params = {};
params.realm_non_active_users = [
{ {
email: 'retiree@example.com', email: 'retiree@example.com',
user_id: 15, user_id: 15,
@@ -885,23 +887,23 @@ run_test('initialize', () => {
}, },
]; ];
global.page_params.realm_users = [ params.realm_users = [
{ {
email: 'alice@example.com', email: 'alice@example.com',
user_id: 16, user_id: 16,
full_name: 'Alice', full_name: 'Alice',
}, },
]; ];
global.page_params.cross_realm_bots = [ params.cross_realm_bots = [
{ {
email: 'bot@example.com', email: 'bot@example.com',
user_id: 17, user_id: 17,
full_name: 'Test Bot', full_name: 'Test Bot',
}, },
]; ];
global.page_params.user_id = 42;
people.initialize(); const my_user_id = 42;
people.initialize(my_user_id, params);
assert.equal(people.get_active_user_for_email('alice@example.com').full_name, 'Alice'); assert.equal(people.get_active_user_for_email('alice@example.com').full_name, 'Alice');
assert.equal(people.is_active_user_for_popover(17), true); assert.equal(people.is_active_user_for_popover(17), true);

View File

@@ -5,7 +5,6 @@ const return_false = function () { return false; };
set_global('server_events', {}); set_global('server_events', {});
set_global('blueslip', {}); set_global('blueslip', {});
set_global('page_params', {});
set_global('reload_state', { set_global('reload_state', {
is_in_progress: return_false, is_in_progress: return_false,
}); });
@@ -161,11 +160,10 @@ run_test('set_presence_info', () => {
}, },
}; };
page_params.presences = presences; const params = {};
page_params.initial_servertime = base_time; params.presences = presences;
presence.initialize(); params.initial_servertime = base_time;
presence.initialize(params);
assert.equal(page_params.presences, undefined);
assert.deepEqual(presence.presence_info.get(alice.user_id), assert.deepEqual(presence.presence_info.get(alice.user_id),
{ status: 'active', last_active: 500} { status: 'active', last_active: 500}

View File

@@ -5,13 +5,16 @@ set_global("page_params", {
{name: "giphy", config: {key: "12345678"}}, {name: "giphy", config: {key: "12345678"}},
{name: "foobot", config: {bar: "baz", qux: "quux"}}, {name: "foobot", config: {bar: "baz", qux: "quux"}},
], ],
});
const bot_data_params = {
realm_bots: [{api_key: 'QadL788EkiottHmukyhHgePUFHREiu8b', realm_bots: [{api_key: 'QadL788EkiottHmukyhHgePUFHREiu8b',
email: 'error-bot@zulip.org', email: 'error-bot@zulip.org',
full_name: 'Error bot', full_name: 'Error bot',
user_id: 1, user_id: 1,
services: []}, services: []},
], ],
}); };
set_global("avatar", {}); set_global("avatar", {});
@@ -26,7 +29,7 @@ set_global('ClipboardJS', function (sel) {
assert.equal(sel, '#copy_zuliprc'); assert.equal(sel, '#copy_zuliprc');
}); });
bot_data.initialize(); bot_data.initialize(bot_data_params);
run_test('generate_zuliprc_uri', () => { run_test('generate_zuliprc_uri', () => {
const uri = settings_bots.generate_zuliprc_uri(1); const uri = settings_bots.generate_zuliprc_uri(1);

View File

@@ -770,27 +770,35 @@ run_test('create_sub', () => {
}); });
run_test('initialize', () => { run_test('initialize', () => {
function initialize() { function get_params() {
page_params.subscriptions = [{ const params = {};
params.subscriptions = [{
name: 'subscriptions', name: 'subscriptions',
stream_id: 2001, stream_id: 2001,
}]; }];
page_params.unsubscribed = [{ params.unsubscribed = [{
name: 'unsubscribed', name: 'unsubscribed',
stream_id: 2002, stream_id: 2002,
}]; }];
page_params.never_subscribed = [{ params.never_subscribed = [{
name: 'never_subscribed', name: 'never_subscribed',
stream_id: 2003, stream_id: 2003,
}]; }];
return params;
}
function initialize() {
stream_data.initialize(get_params());
} }
initialize();
page_params.demote_inactive_streams = 1; page_params.demote_inactive_streams = 1;
page_params.realm_notifications_stream_id = -1; page_params.realm_notifications_stream_id = -1;
stream_data.initialize();
initialize();
assert(!stream_data.is_filtering_inactives()); assert(!stream_data.is_filtering_inactives());
const stream_names = stream_data.get_streams_for_admin().map(elem => elem.name); const stream_names = stream_data.get_streams_for_admin().map(elem => elem.name);
@@ -803,9 +811,8 @@ run_test('initialize', () => {
assert.equal(page_params.notifications_stream, ""); assert.equal(page_params.notifications_stream, "");
// Simulate a private stream the user isn't subscribed to // Simulate a private stream the user isn't subscribed to
initialize();
page_params.realm_notifications_stream_id = 89; page_params.realm_notifications_stream_id = 89;
stream_data.initialize(); initialize();
assert.equal(page_params.notifications_stream, ""); assert.equal(page_params.notifications_stream, "");
// Now actually subscribe the user to the stream // Now actually subscribe the user to the stream
@@ -816,16 +823,17 @@ run_test('initialize', () => {
}; };
stream_data.add_sub(foo); stream_data.add_sub(foo);
stream_data.initialize(); initialize();
assert.equal(page_params.notifications_stream, "foo"); assert.equal(page_params.notifications_stream, "foo");
}); });
run_test('filter inactives', () => { run_test('filter inactives', () => {
page_params.unsubscribed = []; const params = {};
page_params.never_subscribed = []; params.unsubscribed = [];
page_params.subscriptions = []; params.never_subscribed = [];
params.subscriptions = [];
stream_data.initialize(); stream_data.initialize(params);
assert(!stream_data.is_filtering_inactives()); assert(!stream_data.is_filtering_inactives());
page_params.unsubscribed = []; page_params.unsubscribed = [];
@@ -844,7 +852,7 @@ run_test('filter inactives', () => {
}; };
stream_data.add_sub(sub); stream_data.add_sub(sub);
}); });
stream_data.initialize(); stream_data.initialize(params);
assert(stream_data.is_filtering_inactives()); assert(stream_data.is_filtering_inactives());
}); });

View File

@@ -1,5 +1,4 @@
set_global('blueslip', global.make_zblueslip()); set_global('blueslip', global.make_zblueslip());
set_global('page_params', {});
zrequire('user_groups'); zrequire('user_groups');
@@ -9,9 +8,11 @@ run_test('user_groups', () => {
id: 0, id: 0,
members: [1, 2], members: [1, 2],
}; };
global.page_params.realm_user_groups = [students];
user_groups.initialize(); const params = {};
params.realm_user_groups = [students];
user_groups.initialize(params);
assert.equal(user_groups.get_user_group_from_id(students.id), students); assert.equal(user_groups.get_user_group_from_id(students.id), students);
const admins = { const admins = {

View File

@@ -1,15 +1,16 @@
set_global('blueslip', global.make_zblueslip()); set_global('blueslip', global.make_zblueslip());
set_global('channel', {}); set_global('channel', {});
set_global('page_params', {});
zrequire('user_status'); zrequire('user_status');
function initialize() { function initialize() {
page_params.user_status = { const params = {
1: {away: true, status_text: 'in a meeting'}, user_status: {
2: {away: true}, 1: {away: true, status_text: 'in a meeting'},
3: {away: true}, 2: {away: true},
3: {away: true},
},
}; };
user_status.initialize(); user_status.initialize(params);
} }
run_test('basics', () => { run_test('basics', () => {
@@ -23,7 +24,6 @@ run_test('basics', () => {
user_status.revoke_away(4); user_status.revoke_away(4);
assert(!user_status.is_away(4)); assert(!user_status.is_away(4));
// use value from page_params
assert.equal(user_status.get_status_text(1), 'in a meeting'); assert.equal(user_status.get_status_text(1), 'in a meeting');
user_status.set_status_text({ user_status.set_status_text({

View File

@@ -86,12 +86,10 @@ exports.get_services = function bot_data__get_services(bot_id) {
return services.get(bot_id); return services.get(bot_id);
}; };
exports.initialize = function () { exports.initialize = function (params) {
for (const bot of page_params.realm_bots) { for (const bot of params.realm_bots) {
exports.add(bot); exports.add(bot);
} }
delete page_params.realm_bots;
}; };
window.bot_data = exports; window.bot_data = exports;

View File

@@ -1188,27 +1188,23 @@ exports.is_my_user_id = function (user_id) {
return user_id === my_user_id; return user_id === my_user_id;
}; };
exports.initialize = function () { exports.initialize = function (my_user_id, params) {
for (const person of page_params.realm_users) { for (const person of params.realm_users) {
exports.add_in_realm(person); exports.add_in_realm(person);
} }
for (const person of page_params.realm_non_active_users) { for (const person of params.realm_non_active_users) {
exports.add(person); exports.add(person);
} }
for (const person of page_params.cross_realm_bots) { for (const person of params.cross_realm_bots) {
if (!people_dict.has(person.email)) { if (!people_dict.has(person.email)) {
exports.add(person); exports.add(person);
} }
cross_realm_dict.set(person.user_id, person); cross_realm_dict.set(person.user_id, person);
} }
exports.initialize_current_user(page_params.user_id); exports.initialize_current_user(my_user_id);
delete page_params.realm_users; // We are the only consumer of this.
delete page_params.realm_non_active_users;
delete page_params.cross_realm_bots;
}; };
window.people = exports; window.people = exports;

View File

@@ -186,10 +186,9 @@ exports.last_active_date = function (user_id) {
return date; return date;
}; };
exports.initialize = function () { exports.initialize = function (params) {
presence.set_info(page_params.presences, presence.set_info(params.presences,
page_params.initial_servertime); params.initial_servertime);
delete page_params.presences;
}; };
window.presence = exports; window.presence = exports;

View File

@@ -788,8 +788,31 @@ exports.get_streams_for_admin = function () {
return subs; return subs;
}; };
exports.initialize = function () { exports.initialize = function (params) {
color_data.claim_colors(page_params.subscriptions); /*
We get `params` data, which is data that we "own"
and which has already been removed from `page_params`.
We only use it in this function to populate other
data structures.
*/
const subscriptions = params.subscriptions;
const unsubscribed = params.unsubscribed;
const never_subscribed = params.never_subscribed;
/*
We also consume some data directly from `page_params`.
This data can be accessed by any other module,
and we consider the authoritative source to be
`page_params`. Some of this data should eventually
be fully handled by stream_data. In particular,
the way we handle `page_params.realm_default_streams`
is kinda janky, because we maintain our own data
structure, but then some legacy modules still
refer directly to `page_params`. We should fix that.
*/
color_data.claim_colors(subscriptions);
function populate_subscriptions(subs, subscribed, previously_subscribed) { function populate_subscriptions(subs, subscribed, previously_subscribed) {
subs.forEach(function (sub) { subs.forEach(function (sub) {
@@ -803,9 +826,9 @@ exports.initialize = function () {
exports.set_realm_default_streams(page_params.realm_default_streams); exports.set_realm_default_streams(page_params.realm_default_streams);
populate_subscriptions(page_params.subscriptions, true, true); populate_subscriptions(subscriptions, true, true);
populate_subscriptions(page_params.unsubscribed, false, true); populate_subscriptions(unsubscribed, false, true);
populate_subscriptions(page_params.never_subscribed, false, false); populate_subscriptions(never_subscribed, false, false);
// Migrate the notifications stream from the new API structure to // Migrate the notifications stream from the new API structure to
// what the frontend expects. // what the frontend expects.
@@ -824,11 +847,6 @@ exports.initialize = function () {
} }
exports.set_filter_out_inactives(); exports.set_filter_out_inactives();
// Garbage collect data structures that were only used for initialization.
delete page_params.subscriptions;
delete page_params.unsubscribed;
delete page_params.never_subscribed;
}; };
exports.remove_default_stream = function (stream_id) { exports.remove_default_stream = function (stream_id) {

View File

@@ -295,16 +295,133 @@ exports.initialize_kitchen_sink_stuff = function () {
}; };
exports.initialize_everything = function () { exports.initialize_everything = function () {
// initialize other stuff /*
When we initialize our various modules, a lot
of them will consume data from the server
in the form of `page_params`.
The global `page_params` var is basically
a massive dictionary with all the information
that the client needs to run the app. Here
are some examples of what it includes:
- all of the user's user-specific settings
- all realm-specific settings that are
pertinent to the user
- info about streams/subscribers on the realm
- realm settings
- info about all the other users
- some fairly dynamic data, like which of
the other users are "present"
Except for the actual Zulip messages, basically
any data that you see in the app soon after page
load comes from `page_params`.
## Mostly static data
Now, we mostly leave `page_params` intact through
the duration of the app. Most of the data in
`page_params` is fairly static in nature, and we
will simply update it for basic changes like
the following (meant as examples, not gospel):
- I changed my 24-hour time preference.
- The realm admin changed who can edit topics.
- The team's realm icon has changed.
- I switched from day mode to night mode.
Especially for things that are settings-related,
we rarely abstract away the data from `page_params`.
As of this writing, over 90 modules refer directly
to `page_params` for some reason or another.
## Dynamic data
Some of the data in `page_params` is either
more highly dynamic than settings data, or
has more performance requirements than
simple settings data, or both. Examples
include:
- tracking all users (we want to have
multiple Maps to find users, for example)
- tracking all streams
- tracking presence data
- tracking user groups and bots
- tracking recent PMs
Using stream data as an example, we use a
module called `stream_data` to actually track
all the info about the streams that a user
can know about. We populate this module
with data from `page_params`, but thereafter
`stream_data.js` "owns" the stream data:
- other modules should ask `stream_data`
for stuff (and not go to `page_params`)
- when server events come in, they should
be processed by stream_data to update
its own data structures
To help enforce this paradigm, we do the
following:
- only pass `stream_data` what it needs
from `page_params`
- delete the reference to data owned by
`stream_data` in `page_params` itself
*/
function pop_fields(...fields) {
const result = {};
for (const field of fields) {
result[field] = page_params[field];
delete page_params[field];
}
return result;
}
const bot_params = pop_fields(
'realm_bots'
);
const people_params = pop_fields(
'realm_users',
'realm_non_active_users',
'cross_realm_bots'
);
const presence_params = pop_fields(
'presences',
'initial_servertime'
);
const stream_data_params = pop_fields(
'subscriptions',
'unsubscribed',
'never_subscribed'
);
const user_groups_params = pop_fields(
'realm_user_groups'
);
const user_status_params = pop_fields(
'user_status'
);
emojisets.initialize(); emojisets.initialize();
people.initialize(); people.initialize(page_params.user_id, people_params);
scroll_bar.initialize(); scroll_bar.initialize();
message_viewport.initialize(); message_viewport.initialize();
exports.initialize_kitchen_sink_stuff(); exports.initialize_kitchen_sink_stuff();
echo.initialize(); echo.initialize();
stream_color.initialize(); stream_color.initialize();
stream_edit.initialize(); stream_edit.initialize();
stream_data.initialize(); stream_data.initialize(stream_data_params);
pm_conversations.recent.initialize(); pm_conversations.recent.initialize();
muting.initialize(); muting.initialize();
subs.initialize(); subs.initialize();
@@ -320,13 +437,13 @@ exports.initialize_everything = function () {
tab_bar.initialize(); tab_bar.initialize();
} }
server_events.initialize(); server_events.initialize();
user_status.initialize(); user_status.initialize(user_status_params);
compose_pm_pill.initialize(); compose_pm_pill.initialize();
search_pill_widget.initialize(); search_pill_widget.initialize();
reload.initialize(); reload.initialize();
user_groups.initialize(); user_groups.initialize(user_groups_params);
unread.initialize(); unread.initialize();
bot_data.initialize(); // Must happen after people.initialize() bot_data.initialize(bot_params); // Must happen after people.initialize()
message_fetch.initialize(); message_fetch.initialize();
message_scroll.initialize(); message_scroll.initialize();
emoji.initialize(); emoji.initialize();
@@ -346,7 +463,7 @@ exports.initialize_everything = function () {
hashchange.initialize(); hashchange.initialize();
pointer.initialize(); pointer.initialize();
unread_ui.initialize(); unread_ui.initialize();
presence.initialize(); presence.initialize(presence_params);
activity.initialize(); activity.initialize();
emoji_picker.initialize(); emoji_picker.initialize();
compose_fade.initialize(); compose_fade.initialize();

View File

@@ -84,12 +84,10 @@ exports.remove_members = function (user_group_id, user_ids) {
} }
}; };
exports.initialize = function () { exports.initialize = function (params) {
for (const user_group of page_params.realm_user_groups) { for (const user_group of params.realm_user_groups) {
exports.add(user_group); exports.add(user_group);
} }
delete page_params.realm_user_groups; // We are the only consumer of this.
}; };
exports.is_user_group = function (item) { exports.is_user_group = function (item) {

View File

@@ -56,8 +56,8 @@ exports.set_status_text = function (opts) {
user_info.set(opts.user_id, opts.status_text); user_info.set(opts.user_id, opts.status_text);
}; };
exports.initialize = function () { exports.initialize = function (params) {
for (const [str_user_id, dct] of Object.entries(page_params.user_status)) { for (const [str_user_id, dct] of Object.entries(params.user_status)) {
// JSON does not allow integer keys, so we // JSON does not allow integer keys, so we
// convert them here. // convert them here.
const user_id = parseInt(str_user_id, 10); const user_id = parseInt(str_user_id, 10);
@@ -70,8 +70,6 @@ exports.initialize = function () {
user_info.set(user_id, dct.status_text); user_info.set(user_id, dct.status_text);
} }
} }
delete page_params.user_status;
}; };
window.user_status = exports; window.user_status = exports;