While they can share some code, opening the edit panel
for a stream and clearing the panel are pretty different
actions, so we simplify the API for each thing.
You no longer have to pass in booleans, and for the clear
case, you don't have to pass in a bogus node that just
gets ignored.
This fixes a bug where hitting the "n" hotkey was
causing double work related to the hashchange system.
The code is now organized like this:
do_open_create_stream() does the GUI piece
We call the above directly for hash changes.
For in-app actions, whether clicks or hotkeys,
we call open_create_stream(), which delegates
most of the work to do_open_create_stream() but
also updates the hash.
When you unsubscribe a stream by clicking on the
checkmark, we don't want it to disappear right
away, but we also don't need it to stay around
once you start searching for new streams.
Note from Tim: This commit removes some complex code that was just a
workaround for the fact that this widget used to automatically
re-filter immediately after clicking to unsubscribe a user.
Since we've since fixed that original issue, we don't need this.
Earlier, on opening the subs modal, the "Subscribed" tab would be selected
by default when the components.toggle was created for tab switching.
This would change the hash to `#streams/subscribed`, and then extra work had
to be done to change it back to `#streams/all` leading to a longer open times.
With this change, `#streams` and `#streams/subscribed` both take you to
the "Subscribed Tab", and `#streams/all` takes you directly to
the "All Streams" tab.
This pulls the essential bucketing/sorting logic out
of filter_table().
The diff isn't quite as clean as I'd like, but some
of the code that got added back to filter_table() can be
eliminated in the future. Basically, all the stuff
related to hidden ids can just be zapped if we go
to an approach of just re-building the DOM cleanly
whenever our filters change.
We replace two calls to stream_matches_query() with
a single call to triage_stream(), which prevents us
from doing the same is-subscribed checks twice.
We probably should have done this a while ago, even
though these functions are pretty tiny. The goal here
is to make it easier to have more consistent search
semantics.
Our first use case is subs.js. In this case we
are able to decouple a bit of generic string
matching from the subs-specific code.
We move some data code from subs.js to stream_data.js.
It's not clear we have been using the optimal sort for
dealing with locales, but this change preserves the
current behavior. The only subtle change here is that
we look up subs using a Dict now instead of a plain
JS object.
We now render the "skin" part of "Stream Settings" before
adding in the actual streams. The new function
populate_stream_settings_left_panel() takes care of adding
the streams. It uses a new template called
`subscriptions.handlebars`.
Splitting out this function will give us more flexibility
for various improvements.
First, we can decide to render the list after we open the
overlay, just to avoid the problem that users don't know why
the modal's opening. (And we could add a loader spinner as
needed.)
Second, we can improve our filter features so that we do
filtering in the data instead of moving DOM rows around,
which is expensive.
Third, we can eventually introduce progressive rendering.
Finally, having the function broken out will make profiling
more precise about where bottlenecks exist.
This behavior was originally implemented in commit 6993f89, but due to not
specifying a toggle option, the Subscribed/All streams switcher tab was
focused after the input was focused, leading to the input's loss of focus.
Fixes#9981.
This commit prepares the frontend code to be consumed by webpack.
It is a hack: In theory, modules should be declaring and importing the
modules they depend on and the globals they expose directly.
However, that requires significant per-module work, which we don't
really want to block moving our toolchain to webpack on.
So we expose the modules by setting window.varName = varName; as
needed in the js files.
This cleans up some leftover js and css from the effort of
redesign the rows of the #subscriptions table. Redesign happened
in commit 368b5859 and but we forgot to clean up these js and css
pieces.
squash to subs.js.
When admin user create new private stream, widget for changing privacy
of stream doesn't render. Because we render subscription-settings
template partially on subscription-add event, so this case wasn't
handled.
Fixes#9469
We now initialize most modules in ui_init.js, which
isn't the perfect place to do it, but at least now
we have a mostly consolidated entry point.
All the new foo.initialize() methods introduced in
this module run the same order relative to each
other as before this commit. (I did some console
logging with a hacked version of the program to
get the order right.) They happen a bit later than
before, though.
A couple modules still have the `$(function() {`
idiom for miscellaneous reasons:
archive - is a different bundle
common - used elsewhere
list_render - non-standard code style
scroll_bar - no exports
setup - probably special?
socket - $(function () is nested!
transmit - coupled to socket
translations - i18n is a bigger problem
ui_init - this bootstraps everything
This is preparation for enabling an eslint indentation configuration.
90% of these changes are just fixes for indentation errors that have
snuck into the codebase over the years; the others are more
significant reformatting to make eslint happy (that are not otherwise
actually improvements).
The one area that we do not attempt to work on here is the
"switch/case" indentation.
We now have components.toggle simply return an object, without
putting the object into a lookup table. The consumers of the
objects have all been changed to just store the object in their
own module scope.
The diff is a bit hard to read here, but it's mostly de-denting
code and removing these things:
- we don't have opts.name
- we don't have __toggle.lookup
- we don't have keys
- we don't create a sibling object to the prototype object
This commit introduces a helper function called
maybe_select_tab() that goes to the correct tab in the
toggler widget.
It avoids the "lookup" mechanism, which I am hoping to
deprecate, and it handles hypothetical startup issues
by warning instead of crashing.
Before this commit, this sequence would lead to errors:
* Open streams page via the gear menu.
* Go to "All" tab.
* Leave streams settings.
* Re-open stream settings via the gear menu.
After doing this, the tab would show "Subscribed" but the list
would be of all messages.
Now we explicitly goto the first tab.
I added a long comment explaining how subs.js contributed
to this bug--in short, we re-build the widget instead of just
re-opening this.
We may also want the toggle component to simply default the
initial tab to the first tab.
This commit adds a new helper func to check if sub settings tab
is active or not and remove function `add_me_to_member_list`
function from `static/js/stream_edit.js`, cause we don't need to
render subscribers for particular case, as we are already doing that.
Currently, even after unsubscribing from private/public stream
email address of stream is still present in html widgets hidden.
Cause we don't clear email address on unsubscription event.
This clears email address from widget when user unsubscribe
from any stream.
We've had a longstanding bug where the streams settings code
was getting an i18n'ed value in the middle of a callback from
the toggle component, so it would have been broken for
non-English sites. And then a recent cleanup of the toggle
code introduced a bug where the callback-in-the-callback was
getting stale state, so English sites broke too.
This fix just simplifies everything by using the key that
comes into our callback to determine whether we filter or not.
Fixes#8945
This commit is similar to the prior commit, in that we are
more disciplined about setting up handlers. We set them up right
as the widgets get rendered, and the handlers only delegate
up to the container div (id="stream_creation").
We only have one possible email hint, so there's no reason
to create one for each stream row, especially since we don't
clean them out when we close stream settings.
If another user subscribed to or unsubscribed from a stream while the
current user was not subscribed, we previously would attempt to
rerender the subscriber counts for that stream, even though they
weren't rendered at all in the first place; this would trigger
blueslip errors from the expectOne() check in this function.
Fixes#8720.
Even if realm admin can access unsubscribed private stream subscribers,
admin can't add subscriber to stream. Hide the option to add subs to
stream in UI.
This will allow realm admins to access subscribers of unsubscribed
private stream. This is a preparatory commit for letting realm admins
remove those users.
Previously, when creating an invite-only stream, the subscriber counts
were not being rendered properly, in that the "create" event for the
stream had the user not yet subscribed (so can_add_subscribers was
false), and then the rerendering we did when the user susbcribed just
tried to update the number, not actually rerender the thing.
When user click on unsubscribe button, to unsubscribe from private stream
immediately remove unsubscribed stream from all subscribed stream list.
We don't remove public stream immediately on unsubscription, cause
user may want to undo/subscribe back to stream.
Currently, our stream creation do not add stream list to all
stream list. Only subscription-add event add stream list to
stream list. If public stream is created without acting user
subscribed to stream, then newly created stream is not listed
to all stream list.
On stream creation add sub to all stream list table.
This migrates what were effectively data update functions to be called
from the main stream_events handlers, instead of being called from the
view-update code in subs.js.
To re-render stream members on subscription add or remove event,
we are accessing undefined value "active_stream.stream_id" rather
than "active_stream.id", which resulted in falsey value and our
stream members were not updated unconditionally.
This commit prefixes stream names in urls with stream ids,
so that the urls don't break when we rename streams.
strean name: foo bar.com%
before: #narrow/stream/foo.20bar.2Ecom.25
after: #narrow/stream/20-foo-bar.2Ecom.25
For new realms, everything is simple under the new scheme, since
we just parse out the stream id every time to figure out where
to narrow.
For old realms, any old URLs will still work under the new scheme,
assuming the stream hasn't been renamed (and of course old urls
wouldn't have survived stream renaming in the first place). The one
exception is the hopefully rare case of a stream name starting with
something like "99-" and colliding with another stream whose id is 99.
The way that we enocde the stream name portion of the URL is kind
of unimportant now, since we really only look at the stream id, but
we still want a safe encoding of the name that is mostly human
readable, so we now convert spaces to dashes in the stream name. Also,
we try to ensure more code on both sides (frontend and backend) calls
common functions to do the encoding.
Fixes#4713
This will essentially run the code paths to go from whatever you were
at before to /all and back in the case of /new, which will call the
render function three times (!!), so remove this call because it isn’t
really necessary anyways.
Currently the new streams user list will populate twice when you click
the new stream button (or “+”), because it is triggered once directly
by the button click and then once by the hash change to /new, so we
want to ignore the changes by the hash change.
Previously, if you had the streams overlay open (but no active stream
clicked) while another user edited your subscriptions state, we'd
throw an exception handle the get_events call, because the code for
rerendering the subscribers list didn't consider the possibility that
there was no active stream.
When re-subscribing by way of the "Subscribe" button in the right
side settings panel, the row will now be marked as active to
highlight that the row is still selected and being looked at.
Fixes: #6955.
Previously it was called before the event was processed by the server
and the subscription was updated to have the user subscribed to a
stream, so there was a race condition that would make it iso that
sometimes the stream line would disappear on the next render pass due
to the event not having completed yet.
This makes it so that the re-render happens after the event is
processed in `stream_events.js`.
Fixes: #6797.
The current behavior is that when you subscribe to a new stream
the scroll position moves back up to the top because the list updates,
when in reality the user shouldn't notice this, so we record the
previous scroll position and then apply it once the DOM update is
finished before the next paint cycle.
Fixes: #6606.
We used to have code scattered in multiple places to
calculate things like admin options, preview urls,
subscriber counts, and rendered descriptions for
streams before we rendered templates in the "Manage
Stream" code.
These are all consolidated into a new function
called stream_data.update_calculated_fields().
This is mostly code cleanup, but it also fixes a bug where
the "View Stream" button would not work for a newly created
stream.
Despite the length of this commit, it is a very straightforward
moving of code from narrow.js -> narrow_state.js, and then
everything else is just s/narrow.foo()/narrow_state.foo()/
(with a few tiny cleanups to remove some code duplication
in certain callers).
The only new functions are simple setter/getters that
encapsulate the current_filter variable:
narrow_state.reset_current_filter()
narrow_state.set_current_filter()
narrow_state.get_current_filter()
We removed narrow.predicate() as part of this, since it was dead
code.
Also, we removed the shim for narrow_state.set_compose_defaults(),
and since that was the last shim, we removed shim.js from the app.
This code makes the right pane work in "Manage Streams" when
you are editing a stream subscription. It handles basic
functionality (submitting forms, etc.), live updates, and
showing the pane as needed.
Most of the code here was simply moved from subs.js, but some
functions were pulled out of larger functions:
live update:
add_me_to_member_list
update_stream_name
update_stream_description
collapse/show:
collapse
show_sub
We also now export subs.show_subs_pane.
We eventually want stream_edit not to call into subs.js, and
this should be fairly easy--we just need to move some shared
methods to a new module.
This new modules handles the UI to create streams. It mostly moves
code from subs.js.
It introduces an API around what used to be called meta.stream_created:
reset_created_stream()
set_name()
get_name()
It only partially moves new_stream_clicked().
This commit forces the files that create modals to create their own
modal closing function instead of creating all of them in the modals
file. These functions are then passed to the modals.close object. This
is intended to remove modals.js's dependencies on these other files.
This is mostly just moving methods out of compose.js.
The variable `is_composing_message`, which isn't a boolean, has
been renamed to `message_type`, and there are new functions
set_message_type() and get_message_type() that wrap it.
This commit removes some shims related to the global variable
`compose_state`; now, `compose_state` is a typical global
variable with a 1:1 relationship with the module by the same
name.
The new module has 100% line coverage, most of it coming
via the tests on compose_actions.js. (The methods here are
super simple, so it's a good thing that the tests are somewhat
integrated with a higher layer.)
Prevent switching of stream rows on pressing arrow key when focussed
on the 'Create stream' section (this would cancel the curren stream
creation flow).
The subscriber data is currently pulled from the web needlessly
when it exists in memory. This processes data returned from
the `stream_data.get_sub_by_id(n).subscribers` and the
`people.get_person_from_user_id(n).email` methods to build the
same list of subscribers that is sent from the server.
Fixes: #4314.
On filling out the name and description for new stream,
and changing the tab (e.g. by clicking on a stream on the left),
then come back to 'Create streams', it should restore stream name
similar to the stream description.
Fix#4311.
This is kinda hacky and probably not how we want this to work
long-term, but I think it's a larger refactoring project to make this
part of the model make sense.
If a url is present in stream description, it will be
rendered as a clickable link under /streams page.
Tweaked by tabbott to use the separate rendered_description element to
avoid duplicate rendering and to live-update.
Fixes#1435.
On realms with ``should_list_all_streams() == False``, previously, we
would subscribe a user to a stream, but also incorrectly show the stream
creation dialog.
Instead, we act as if the stream was newly created.
This consolidates all actions to close modals into modals.js and
triggers the correct cleaning/collapsing function dependent on what the
data-overlay attribute is labeled as.
It also ensures these all have an e.stopPropagation().
Fixes#4029.
This fixes the mobile web experience for Chrome on iOS.
Apparently, Chrome-on-iOS silently has a `viewport` module that
overrides and user-defined module by that name, causing all of our
code that accesses the viewport module to not work on that platform.
We fix this by renaming it.
We pass in sub instead of stream_name, to support callers that
already do lookups by stream id.
And then we make the second optional argument be subscribers, since
that is all we were using from the old `attrs` argument.
When possible, we should use get_sub_from_target() instead of
get_stream_name(), not only because it often saves a lookup later,
but it also makes it easier to audit the code for name vs. id
bugs. (When you rename a stream, there can be races where you
use the old stream name instead of the more durable stream_id).
This commit handles the easy cases where the caller directly
wanted the sub, not the name.
Previously the mechanism worked such that the innerHTML was being
appended to directly potentially thousands of times which has horrific
performance implications. By concating all the strings together before
appending to the HTML it all gets rendered in one chunk without forcing
a re-render of previous elements. Performance is ~15x-20x faster now.
Change the remaining "Admin settings" with a button, namely
changing a stream's privacy, to instead be a "[Change]" link
opening a confirmation modal.
Fixes: #3493.
User search for streams will now return results where the stream
description (but not the stream name) include the string in the
user query.
The filtering process first obtains the streams whose names match the
user search query, then sorts and displays them. From the remaining
streams, it obtains streams whose description matches the query and
displays them in sorted order after the name match results. Other
streams are not displayed.
Fixes: #2674.
This is a fairly risky, invasive change that speeds up
stream deactivation by no longer sending subscription/remove
events for individual subscribers to all of the clients who
care about a stream. Instead, we let the client handle the
stream deactivation on a coarser level.
The back end changes here are pretty straightforward.
On the front end we handle stream deactivations by removing the
stream (as needed) from the streams sidebar and/or the stream
settings page. We also remove the stream from the internal data
structures.
There may be some edge cases where live updates don't handle
everything, such as if you are about to compose a message to a
stream that has been deactivated. These should be rare, as admins
generally deactivate streams that have been dormant, and they
should be recoverable either by getting proper error handling when
you try to send to the stream or via reload.
This makes the subscriptions page responsive by having the settings tab
slide over when a user taps on a stream, giving almost the whole screen
to view the settings.
When filtering streams, we were incorrectly treating the regexp input
provided by the user as a regular expression, meaning that terms like
`c++` would trigger errors because they are invalid regular expression
syntax. We fix this by replacing RegExp with a simple IndexOf check.
Node test added by tabbott.
Fixes#3559.
This changes all references of the data-stream-name to more
predictable data-stream-id references in the subscriptions overlay.
This prevents unescaped characters from breaking selectors and stream
renames from breaking selectors.
This change makes it so that when you are creating a stream
and use "Copy from Stream", the UI will immediately
check/uncheck the user checkboxes that correspond to the
stream's subscribers.
In concrete terms this allows Cordelia to create a new
stream call "Paris" that has all the "Verona" subscribers
except for Hamlet.
It also makes it so that when you go to create the stream,
the response is a little quicker, because we don't have to
iterate the streams.
Finally, it removes an odd quirk from the original design,
where if you clicked on Denmark but then collapsed the
streams, we wouldn't actually add the Denmark subscribers
to your new stream.
The current UI will still be slightly intuitive for people, as
I think checkmarks don't really make sense here. What we
really want are Add/Remove links (or buttons) next to each
of the existing streams.
I moved the UI element for "Copy from Stream" to be above
the list of users, including the filter box and check/uncheck
links, which no longer get applied to the list of streams.
The reason I no longer apply the filter to streams is...
* It's kind of confusing to have filters apply to both
streams and users. There should be separate filters for
them, and I will try to resuscitate that feature later.
* The code to filter the streams was doing a sketchy
regex operation against user-inputted data. (`match()`)
* We want to use the same stream filtering code as the
right sidebar uses.
* It improves performance for the common case that you
are filtering users.
The reason I no longer apply the check-all/uncheck-all actions
to streams is that it would be crazy to select all your streams
to copy users from, and it would be expensive/slow for large
realms, and it would likely be done by accident if somebody was
trying to manage individual users.
Finally, the check-all/uncheck-all actions have been scoped
to the users filtered by the text box, so I moved the links
under the text box to make that hopefully more clear to users.
If we blank out the user filter for users (by hitting backspace,
for example), then we now have short-circuit logic to display all
the user checkboxes. (The user-facing behavior doesn't change here,
but now we don't have to process all the strings.)
The function people.filter_by_search_terms() used
to return a JS object with emails as keys to represent
a set of users. Now we return a Zulip Dict() object
with user_ids as keys.
The old implementation was O(N squared) for N = number of
users due to its using an O(N) selector inside of a loop.
Now we simply iterate through all the checkboxes and turn them
on or off based on a bunch of O(1) operations.
When we subscribe ourselves using the "Add" button in the
right pane of "Stream settings", we now call
stream_data.subscribe_myself(), which properly updates our
data structures (more than just sub.subscribed) and prevents
some console errors when you un-subscribe yourself using
the check mark.
This change makes it such that the stream filtering operation will only
run if the subscription overlay is visible, preventing any issues with
the lack of existence of elements or processing something that users
won’t be able to see.
Fixes#3388.
The new subs.close() function should unify all closing events of the
subscriptions overlay. The function also now tracks whether the
subscription overlay is in a closed or open state.
- Change `stream_name` into `stream_id` on some API endpoints that use
`stream_name` in their URLs to prevent confusion of `views` selection.
For example:
If the stream name is "foo/members", the URL would be trigger
"^streams/(?P<stream_name>.*)/members$" and it would be confusing because
we intend to use the endpoint with "^streams/(?P<stream_name>.*)$" regex.
All stream-related endpoints now use stream id instead of stream name,
except for a single endpoint that lets you convert stream names to stream ids.
See https://github.com/zulip/zulip/issues/2930#issuecomment-269576231
- Add `get_stream_id()` method to Zulip API client, and change
`get_subscribers()` method to comply with the new stream API
(replace `stream_name` with `stream_id`).
Fixes#2930.
When filtering users in the new stream form, check all
and uncheck all will only effect those users who are filtered,
visible in the dom.
Includes a Casper test for the new condition.
When we change a stream name, we now use the stream id as the
key to find messages we need to live update. This eliminates
some possible race conditions from two users renaming a stream.
This commit introduces message_live_update.js.
The new call stack is this:
subs.update_subscription_properties
subs.update_stream_name
message_live_update.update_stream_name
message_list.update_stream_name