mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
We now use the same code in all places to get the bucket of user_ids that correspond to a stream, and we consistently treat a stream as having zero subscribers, not an undefined number of subscribers, in the hypothetical case of us asking about a stream that we're not tracking. The behavior for untracked streams has always been problematic, since if a stream is untracked, all bets are off. So now if we don't "track" the stream, the subscriber count is zero. None of our callers distinguish between undefined and zero. And we just consider the stream to be subscribed by a user when add_subscriber is called, even if we haven't been told by stream_data to track the stream. (We also stop returning true/false from add_subscriber, since only test code was looking at it.) We protect against the most likely source of internal-to-the-frontend bugs by adding the assert_number() call. We generally have to assume that the server is sending us sensible data at page load time, or all bets are off. And we have good protections in place for unknown ids in our dispatch code for peer_add/peer_remove events.
152 lines
4.6 KiB
JavaScript
152 lines
4.6 KiB
JavaScript
const {LazySet} = require("./lazy_set");
|
|
const people = require("./people");
|
|
|
|
// This maps a stream_id to a LazySet of user_ids who are subscribed.
|
|
// We maintain the invariant that this has keys for all all stream_ids
|
|
// that we track in the other data structures. We intialize it during
|
|
// clear_subscriptions.
|
|
let stream_subscribers;
|
|
|
|
function assert_number(id) {
|
|
if (typeof id !== "number") {
|
|
blueslip.error(`You must pass ids as numbers to peer_data. id = ${id}`);
|
|
}
|
|
}
|
|
|
|
function get_user_set(stream_id) {
|
|
assert_number(stream_id);
|
|
|
|
// This is an internal function to get the LazySet of users.
|
|
// We create one on the fly as necessary, but we warn in that case.
|
|
let subscribers = stream_subscribers.get(stream_id);
|
|
|
|
if (subscribers === undefined) {
|
|
blueslip.warn("We called get_user_set for an untracked stream: " + stream_id);
|
|
subscribers = new LazySet([]);
|
|
stream_subscribers.set(stream_id, subscribers);
|
|
}
|
|
|
|
return subscribers;
|
|
}
|
|
|
|
export function clear() {
|
|
stream_subscribers = new Map();
|
|
}
|
|
|
|
export function maybe_clear_subscribers(stream_id) {
|
|
if (!stream_subscribers.has(stream_id)) {
|
|
set_subscribers(stream_id, []);
|
|
}
|
|
}
|
|
|
|
export function is_subscriber_subset(stream_id1, stream_id2) {
|
|
const sub1_set = get_user_set(stream_id1);
|
|
const sub2_set = get_user_set(stream_id2);
|
|
|
|
return Array.from(sub1_set.keys()).every((key) => sub2_set.has(key));
|
|
}
|
|
|
|
export function potential_subscribers(stream_id) {
|
|
/*
|
|
This is a list of unsubscribed users
|
|
for the current stream, who the current
|
|
user could potentially subscribe to the
|
|
stream. This may include some bots.
|
|
|
|
We currently use it for typeahead in
|
|
stream_edit.js.
|
|
|
|
This may be a superset of the actual
|
|
subscribers that you can change in some cases
|
|
(like if you're a guest?); we should refine this
|
|
going forward, especially if we use it for something
|
|
other than typeahead. (The guest use case
|
|
may be moot now for other reasons.)
|
|
*/
|
|
|
|
const subscribers = stream_subscribers.get(stream_id);
|
|
|
|
function is_potential_subscriber(person) {
|
|
// Use verbose style to force better test
|
|
// coverage, plus we may add more conditions over
|
|
// time.
|
|
if (subscribers.has(person.user_id)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return people.filter_all_users(is_potential_subscriber);
|
|
}
|
|
|
|
export function get_subscriber_count(stream_id) {
|
|
const subscribers = get_user_set(stream_id);
|
|
return subscribers.size;
|
|
}
|
|
|
|
export function get_subscribers(stream_id) {
|
|
// This is our external interface for callers who just
|
|
// want an array of user_ids who are subscribed to a stream.
|
|
const subscribers = get_user_set(stream_id);
|
|
|
|
return Array.from(subscribers.keys());
|
|
}
|
|
|
|
export function set_subscribers(stream_id, user_ids) {
|
|
const subscribers = new LazySet(user_ids || []);
|
|
stream_subscribers.set(stream_id, subscribers);
|
|
return subscribers;
|
|
}
|
|
|
|
export function add_subscriber(stream_id, user_id) {
|
|
// If stream_id/user_id are unknown to us, we will
|
|
// still track it, but we will warn.
|
|
const subscribers = get_user_set(stream_id);
|
|
const person = people.get_by_user_id(user_id);
|
|
if (person === undefined) {
|
|
blueslip.warn("We tried to add invalid subscriber: " + user_id);
|
|
}
|
|
subscribers.add(user_id);
|
|
}
|
|
|
|
export function remove_subscriber(stream_id, user_id) {
|
|
const subscribers = get_user_set(stream_id);
|
|
if (!subscribers.has(user_id)) {
|
|
blueslip.warn("We tried to remove invalid subscriber: " + user_id);
|
|
return false;
|
|
}
|
|
|
|
subscribers.delete(user_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
export function bulk_add_subscribers({stream_ids, user_ids}) {
|
|
// We rely on our callers to validate stream_ids and user_ids.
|
|
for (const stream_id of stream_ids) {
|
|
const subscribers = get_user_set(stream_id);
|
|
for (const user_id of user_ids) {
|
|
subscribers.add(user_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function bulk_remove_subscribers({stream_ids, user_ids}) {
|
|
// We rely on our callers to validate stream_ids and user_ids.
|
|
for (const stream_id of stream_ids) {
|
|
const subscribers = get_user_set(stream_id);
|
|
for (const user_id of user_ids) {
|
|
subscribers.delete(user_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function is_user_subscribed(stream_id, user_id) {
|
|
// Most callers should call stream_data.is_user_subscribed,
|
|
// which does additional checks.
|
|
|
|
const subscribers = get_user_set(stream_id);
|
|
return subscribers.has(user_id);
|
|
}
|