2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-02-28 21:16:38 +01:00
|
|
|
import {LazySet} from "./lazy_set";
|
2023-07-08 09:46:05 +02:00
|
|
|
import type {User} from "./people";
|
2021-02-28 21:16:38 +01:00
|
|
|
import * as people from "./people";
|
2021-04-15 17:02:54 +02:00
|
|
|
import * as sub_store from "./sub_store";
|
2021-02-28 00:53:59 +01:00
|
|
|
|
2021-01-12 21:38:01 +01:00
|
|
|
// This maps a stream_id to a LazySet of user_ids who are subscribed.
|
2023-07-08 09:46:05 +02:00
|
|
|
const stream_subscribers = new Map<number, LazySet>();
|
2021-01-12 21:38:01 +01:00
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function clear_for_testing(): void {
|
2021-03-09 16:02:52 +01:00
|
|
|
stream_subscribers.clear();
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
function get_user_set(stream_id: number): LazySet {
|
2021-01-29 17:17:32 +01:00
|
|
|
// 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.
|
2021-04-15 17:02:54 +02:00
|
|
|
if (!sub_store.get(stream_id)) {
|
2023-07-08 09:46:05 +02:00
|
|
|
blueslip.warn(`We called get_user_set for an untracked stream: ${stream_id}`);
|
2021-01-30 13:38:55 +01:00
|
|
|
}
|
|
|
|
|
2021-01-29 17:17:32 +01:00
|
|
|
let subscribers = stream_subscribers.get(stream_id);
|
|
|
|
|
|
|
|
if (subscribers === undefined) {
|
|
|
|
subscribers = new LazySet([]);
|
|
|
|
stream_subscribers.set(stream_id, subscribers);
|
|
|
|
}
|
|
|
|
|
|
|
|
return subscribers;
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function is_subscriber_subset(stream_id1: number, stream_id2: number): boolean {
|
2021-01-29 17:17:32 +01:00
|
|
|
const sub1_set = get_user_set(stream_id1);
|
|
|
|
const sub2_set = get_user_set(stream_id2);
|
2021-01-12 21:38:01 +01:00
|
|
|
|
2023-03-02 01:58:25 +01:00
|
|
|
return [...sub1_set.keys()].every((key) => sub2_set.has(key));
|
2021-01-12 21:38:01 +01:00
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function potential_subscribers(stream_id: number): User[] {
|
2021-01-12 21:38:01 +01:00
|
|
|
/*
|
|
|
|
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.)
|
|
|
|
*/
|
|
|
|
|
2021-01-30 13:51:21 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-12 21:38:01 +01:00
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
function is_potential_subscriber(person: User): boolean {
|
2021-01-12 21:38:01 +01:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function get_subscriber_count(stream_id: number): number {
|
2021-01-29 17:17:32 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-12 21:38:01 +01:00
|
|
|
return subscribers.size;
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function get_subscribers(stream_id: number): number[] {
|
2021-01-29 17:17:32 +01:00
|
|
|
// 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);
|
2021-01-12 21:38:01 +01:00
|
|
|
|
2023-03-02 01:58:25 +01:00
|
|
|
return [...subscribers.keys()];
|
2021-01-12 21:38:01 +01:00
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function set_subscribers(stream_id: number, user_ids: number[]): void {
|
|
|
|
const subscribers = new LazySet(user_ids);
|
2021-01-13 22:03:25 +01:00
|
|
|
stream_subscribers.set(stream_id, subscribers);
|
2021-01-12 21:38:01 +01:00
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function add_subscriber(stream_id: number, user_id: number): void {
|
2021-01-29 17:17:32 +01:00
|
|
|
// 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);
|
2023-06-16 15:23:45 +02:00
|
|
|
const person = people.maybe_get_user_by_id(user_id);
|
2021-01-12 21:38:01 +01:00
|
|
|
if (person === undefined) {
|
2023-07-08 09:46:05 +02:00
|
|
|
blueslip.warn(`We tried to add invalid subscriber: ${user_id}`);
|
2021-01-12 21:38:01 +01:00
|
|
|
}
|
|
|
|
subscribers.add(user_id);
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function remove_subscriber(stream_id: number, user_id: number): boolean {
|
2021-01-29 17:17:32 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-12 21:38:01 +01:00
|
|
|
if (!subscribers.has(user_id)) {
|
2023-07-08 09:46:05 +02:00
|
|
|
blueslip.warn(`We tried to remove invalid subscriber: ${user_id}`);
|
2021-01-12 21:38:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
subscribers.delete(user_id);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function bulk_add_subscribers({
|
|
|
|
stream_ids,
|
|
|
|
user_ids,
|
|
|
|
}: {
|
|
|
|
stream_ids: number[];
|
|
|
|
user_ids: number[];
|
|
|
|
}): void {
|
2021-01-27 15:54:06 +01:00
|
|
|
// We rely on our callers to validate stream_ids and user_ids.
|
|
|
|
for (const stream_id of stream_ids) {
|
2021-01-29 17:17:32 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-27 15:54:06 +01:00
|
|
|
for (const user_id of user_ids) {
|
|
|
|
subscribers.add(user_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function bulk_remove_subscribers({
|
|
|
|
stream_ids,
|
|
|
|
user_ids,
|
|
|
|
}: {
|
|
|
|
stream_ids: number[];
|
|
|
|
user_ids: number[];
|
|
|
|
}): void {
|
2021-01-27 15:54:06 +01:00
|
|
|
// We rely on our callers to validate stream_ids and user_ids.
|
|
|
|
for (const stream_id of stream_ids) {
|
2021-01-29 17:17:32 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-27 15:54:06 +01:00
|
|
|
for (const user_id of user_ids) {
|
|
|
|
subscribers.delete(user_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:46:05 +02:00
|
|
|
export function is_user_subscribed(stream_id: number, user_id: number): boolean {
|
2021-01-12 21:38:01 +01:00
|
|
|
// Most callers should call stream_data.is_user_subscribed,
|
|
|
|
// which does additional checks.
|
|
|
|
|
2021-01-29 17:17:32 +01:00
|
|
|
const subscribers = get_user_set(stream_id);
|
2021-01-12 21:38:01 +01:00
|
|
|
return subscribers.has(user_id);
|
|
|
|
}
|