2021-03-16 23:38:59 +01:00
import * as blueslip from "./blueslip" ;
2021-02-28 00:53:59 +01:00
import * as color _data from "./color_data" ;
import { FoldDict } from "./fold_dict" ;
2021-04-13 06:51:54 +02:00
import { $t } from "./i18n" ;
2021-03-25 22:35:45 +01:00
import { page _params } from "./page_params" ;
2021-02-28 00:53:59 +01:00
import * as peer _data from "./peer_data" ;
import * as people from "./people" ;
import * as settings _config from "./settings_config" ;
2021-02-28 00:54:32 +01:00
import * as stream _topic _history from "./stream_topic_history" ;
2021-04-15 16:35:14 +02:00
import * as sub _store from "./sub_store" ;
2021-07-28 16:00:58 +02:00
import { user _settings } from "./user_settings" ;
2021-02-28 00:53:59 +01:00
import * as util from "./util" ;
2019-02-08 11:56:33 +01:00
2021-03-28 16:51:24 +02:00
const DEFAULT _COLOR = "#c2c2c2" ;
2021-01-12 21:38:01 +01:00
// Expose get_subscriber_count for our automated puppeteer tests.
2021-02-28 00:53:59 +01:00
export const get _subscriber _count = peer _data . get _subscriber _count ;
2021-01-12 21:38:01 +01:00
2020-07-23 02:42:29 +02:00
class BinaryDict {
2019-12-27 15:56:46 +01:00
/ *
A dictionary that keeps track of which objects had the predicate
return true or false for efficient lookups and iteration .
This class is an optimization for managing subscriptions .
Typically you only subscribe to a small minority of streams , and
most common operations want to efficiently iterate through only
streams where the current user is subscribed :
- search bar search
- build left sidebar
- autocomplete # stream _links
- autocomplete stream in compose
* /
2020-07-23 02:42:29 +02:00
trues = new FoldDict ( ) ;
falses = new FoldDict ( ) ;
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
constructor ( pred ) {
this . pred = pred ;
}
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
true _values ( ) {
return this . trues . values ( ) ;
}
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
num _true _items ( ) {
return this . trues . size ;
}
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
false _values ( ) {
return this . falses . values ( ) ;
}
* values ( ) {
for ( const value of this . trues . values ( ) ) {
2020-02-03 08:51:09 +01:00
yield value ;
}
2020-07-23 02:42:29 +02:00
for ( const value of this . falses . values ( ) ) {
2020-02-03 08:51:09 +01:00
yield value ;
}
2020-07-23 02:42:29 +02:00
}
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
get ( k ) {
const res = this . trues . get ( k ) ;
2019-12-27 15:56:46 +01:00
if ( res !== undefined ) {
return res ;
}
2020-07-23 02:42:29 +02:00
return this . falses . get ( k ) ;
}
2019-12-27 15:56:46 +01:00
2020-07-23 02:42:29 +02:00
set ( k , v ) {
if ( this . pred ( v ) ) {
this . set _true ( k , v ) ;
2019-12-27 15:56:46 +01:00
} else {
2020-07-23 02:42:29 +02:00
this . set _false ( k , v ) ;
2019-12-27 15:56:46 +01:00
}
2020-07-23 02:42:29 +02:00
}
set _true ( k , v ) {
this . falses . delete ( k ) ;
this . trues . set ( k , v ) ;
}
set _false ( k , v ) {
this . trues . delete ( k ) ;
this . falses . set ( k , v ) ;
}
delete ( k ) {
this . trues . delete ( k ) ;
this . falses . delete ( k ) ;
}
}
2013-09-16 23:47:05 +02:00
// The stream_info variable maps stream names to stream properties objects
// Call clear_subscriptions() to initialize it.
2019-11-02 00:06:25 +01:00
let stream _info ;
let filter _out _inactives = false ;
2013-09-16 23:47:05 +02:00
2019-12-26 15:34:17 +01:00
const stream _ids _by _name = new FoldDict ( ) ;
2019-12-30 13:20:45 +01:00
const default _stream _ids = new Set ( ) ;
2017-05-11 21:49:38 +02:00
2021-02-28 00:53:59 +01:00
export const stream _privacy _policy _values = {
2020-07-10 14:25:21 +02:00
public : {
code : "public" ,
2021-04-13 06:51:54 +02:00
name : $t ( { defaultMessage : "Public" } ) ,
description : $t ( {
defaultMessage :
2021-09-14 20:16:15 +02:00
"Organization members can join (guests must be invited by a subscriber); organization members can view complete message history without joining" ,
2021-04-13 06:51:54 +02:00
} ) ,
2020-07-10 14:25:21 +02:00
} ,
private _with _public _history : {
code : "invite-only-public-history" ,
2021-04-13 06:51:54 +02:00
name : $t ( { defaultMessage : "Private, shared history" } ) ,
description : $t ( {
defaultMessage :
2021-09-14 20:16:15 +02:00
"Must be invited by a subscriber; new subscribers can view complete message history; hidden from non-administrator users" ,
2021-04-13 06:51:54 +02:00
} ) ,
2020-07-10 14:25:21 +02:00
} ,
private : {
code : "invite-only" ,
2021-04-13 06:51:54 +02:00
name : $t ( { defaultMessage : "Private, protected history" } ) ,
description : $t ( {
defaultMessage :
2021-09-14 20:16:15 +02:00
"Must be invited by a subscriber; new subscribers can only see messages sent after they join; hidden from non-administrator users" ,
2021-04-13 06:51:54 +02:00
} ) ,
2020-07-10 14:25:21 +02:00
} ,
2021-12-01 23:04:43 +01:00
web _public : {
2021-05-20 13:02:00 +02:00
code : "web-public" ,
name : $t ( { defaultMessage : "Web public" } ) ,
description : $t ( {
defaultMessage :
2021-09-14 20:16:15 +02:00
"Organization members can join (guests must be invited by a subscriber); anyone on the Internet can view complete message history without creating an account" ,
2021-05-20 13:02:00 +02:00
} ) ,
2021-12-01 23:04:43 +01:00
} ,
} ;
2021-05-20 13:02:00 +02:00
2021-02-28 00:53:59 +01:00
export const stream _post _policy _values = {
2020-02-04 21:50:55 +01:00
everyone : {
code : 1 ,
2021-04-13 06:51:54 +02:00
description : $t ( { defaultMessage : "All stream members can post" } ) ,
2020-02-04 21:50:55 +01:00
} ,
admins : {
code : 2 ,
2021-04-13 06:51:54 +02:00
description : $t ( { defaultMessage : "Only organization administrators can post" } ) ,
2020-02-04 21:50:55 +01:00
} ,
2021-05-01 16:19:33 +02:00
moderators : {
code : 4 ,
description : $t ( {
defaultMessage : "Only organization administrators and moderators can post" ,
} ) ,
} ,
2020-02-04 21:50:55 +01:00
non _new _members : {
code : 3 ,
2021-04-13 06:51:54 +02:00
description : $t ( { defaultMessage : "Only organization full members can post" } ) ,
2020-02-04 21:50:55 +01:00
} ,
} ;
2021-02-28 00:53:59 +01:00
export function clear _subscriptions ( ) {
2021-01-12 16:05:24 +01:00
// This function is only used once at page load, and then
// it should only be used in tests.
2020-07-02 01:45:54 +02:00
stream _info = new BinaryDict ( ( sub ) => sub . subscribed ) ;
2021-04-15 16:35:14 +02:00
sub _store . clear ( ) ;
2021-02-28 00:53:59 +01:00
}
2016-06-04 22:40:25 +02:00
2021-02-28 00:53:59 +01:00
clear _subscriptions ( ) ;
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function set _filter _out _inactives ( ) {
2020-07-15 00:34:28 +02:00
if (
2021-07-28 16:00:58 +02:00
user _settings . demote _inactive _streams ===
2020-07-15 00:34:28 +02:00
settings _config . demote _inactive _streams _values . automatic . code
) {
2021-02-28 00:53:59 +01:00
filter _out _inactives = num _subscribed _subs ( ) >= 30 ;
2020-07-15 00:34:28 +02:00
} else if (
2021-07-28 16:00:58 +02:00
user _settings . demote _inactive _streams ===
2020-07-15 00:34:28 +02:00
settings _config . demote _inactive _streams _values . always . code
) {
2019-03-17 14:48:51 +01:00
filter _out _inactives = true ;
} else {
filter _out _inactives = false ;
}
2021-02-28 00:53:59 +01:00
}
2019-02-11 22:05:59 +01:00
// for testing:
2021-02-28 00:53:59 +01:00
export function is _filtering _inactives ( ) {
2019-02-11 22:05:59 +01:00
return filter _out _inactives ;
2021-02-28 00:53:59 +01:00
}
2019-02-11 22:05:59 +01:00
2021-02-28 00:53:59 +01:00
export function is _active ( sub ) {
2019-03-17 14:48:51 +01:00
if ( ! filter _out _inactives || sub . pin _to _top ) {
2019-02-11 22:05:59 +01:00
// If users don't want to filter inactive streams
// to the bottom, we respect that setting and don't
// treat any streams as dormant.
//
// Currently this setting is automatically determined
// by the number of streams. See the callers
// to set_filter_out_inactives.
return true ;
}
2020-03-22 18:40:05 +01:00
return stream _topic _history . stream _has _topics ( sub . stream _id ) || sub . newly _subscribed ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function rename _sub ( sub , new _name ) {
2019-11-02 00:06:25 +01:00
const old _name = sub . name ;
2017-05-11 21:49:38 +02:00
stream _ids _by _name . set ( old _name , sub . stream _id ) ;
2016-10-30 17:33:23 +01:00
sub . name = new _name ;
2020-02-03 07:41:38 +01:00
stream _info . delete ( old _name ) ;
2016-10-30 17:33:23 +01:00
stream _info . set ( new _name , sub ) ;
2021-02-28 00:53:59 +01:00
}
2016-10-30 17:33:23 +01:00
2021-02-28 00:53:59 +01:00
export function subscribe _myself ( sub ) {
2019-11-02 00:06:25 +01:00
const user _id = people . my _current _user _id ( ) ;
2021-01-12 21:38:01 +01:00
peer _data . add _subscriber ( sub . stream _id , user _id ) ;
2017-01-20 23:04:40 +01:00
sub . subscribed = true ;
2017-04-28 15:59:30 +02:00
sub . newly _subscribed = true ;
2019-12-27 15:56:46 +01:00
stream _info . set _true ( sub . name , sub ) ;
2021-02-28 00:53:59 +01:00
}
2017-01-20 23:04:40 +01:00
2021-02-28 00:53:59 +01:00
export function unsubscribe _myself ( sub ) {
2016-11-09 16:26:35 +01:00
// Remove user from subscriber's list
2019-11-02 00:06:25 +01:00
const user _id = people . my _current _user _id ( ) ;
2021-01-12 21:38:01 +01:00
peer _data . remove _subscriber ( sub . stream _id , user _id ) ;
2016-11-09 16:26:35 +01:00
sub . subscribed = false ;
2017-04-28 15:59:30 +02:00
sub . newly _subscribed = false ;
2019-12-27 15:56:46 +01:00
stream _info . set _false ( sub . name , sub ) ;
2021-02-28 00:53:59 +01:00
}
2016-11-09 16:26:35 +01:00
2021-02-28 00:53:59 +01:00
export function add _sub ( sub ) {
2021-01-12 20:58:11 +01:00
// This function is currently used only by tests.
// We use create_sub_from_server_data at page load.
// We use create_streams for new streams in live-update events.
2020-02-09 22:02:55 +01:00
stream _info . set ( sub . name , sub ) ;
2021-04-15 16:35:14 +02:00
sub _store . add _hydrated _sub ( sub . stream _id , sub ) ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function get _sub ( stream _name ) {
2013-09-16 23:47:05 +02:00
return stream _info . get ( stream _name ) ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function get _stream _id ( name ) {
2017-05-11 21:49:38 +02:00
// Note: Only use this function for situations where
// you are comfortable with a user dealing with an
// old name of a stream (from prior to a rename).
2019-11-02 00:06:25 +01:00
const sub = stream _info . get ( name ) ;
2017-05-11 21:49:38 +02:00
if ( sub ) {
return sub . stream _id ;
}
2019-11-02 00:06:25 +01:00
const stream _id = stream _ids _by _name . get ( name ) ;
2017-05-11 21:49:38 +02:00
return stream _id ;
2021-02-28 00:53:59 +01:00
}
2017-05-11 21:49:38 +02:00
2021-02-28 00:53:59 +01:00
export function get _sub _by _name ( name ) {
2017-05-11 21:49:38 +02:00
// Note: Only use this function for situations where
// you are comfortable with a user dealing with an
// old name of a stream (from prior to a rename).
2019-11-02 00:06:25 +01:00
const sub = stream _info . get ( name ) ;
2017-05-11 21:49:38 +02:00
if ( sub ) {
return sub ;
}
2019-11-02 00:06:25 +01:00
const stream _id = stream _ids _by _name . get ( name ) ;
2017-05-11 21:49:38 +02:00
if ( ! stream _id ) {
2020-09-24 07:50:36 +02:00
return undefined ;
2017-05-11 21:49:38 +02:00
}
2021-04-15 16:35:14 +02:00
return sub _store . get ( stream _id ) ;
2021-02-28 00:53:59 +01:00
}
2017-05-11 21:49:38 +02:00
2021-02-28 00:53:59 +01:00
export function id _to _slug ( stream _id ) {
let name = maybe _get _stream _name ( stream _id ) || "unknown" ;
2018-12-14 19:02:26 +01:00
// The name part of the URL doesn't really matter, so we try to
// make it pretty.
2020-07-15 01:29:15 +02:00
name = name . replace ( " " , "-" ) ;
2018-12-14 19:02:26 +01:00
2020-07-15 01:29:15 +02:00
return stream _id + "-" + name ;
2021-02-28 00:53:59 +01:00
}
2018-12-14 19:02:26 +01:00
2021-02-28 00:53:59 +01:00
export function name _to _slug ( name ) {
const stream _id = get _stream _id ( name ) ;
2018-02-15 21:02:47 +01:00
if ( ! stream _id ) {
return name ;
}
// The name part of the URL doesn't really matter, so we try to
// make it pretty.
2020-07-15 01:29:15 +02:00
name = name . replace ( " " , "-" ) ;
2018-02-15 21:02:47 +01:00
2020-07-15 01:29:15 +02:00
return stream _id + "-" + name ;
2021-02-28 00:53:59 +01:00
}
2018-02-15 21:02:47 +01:00
2021-02-28 00:53:59 +01:00
export function slug _to _name ( slug ) {
2020-07-03 16:37:16 +02:00
/ *
Modern stream slugs look like this , where 42
is a stream id :
42
42 - stream - name
We have legacy slugs that are just the name
of the stream :
stream - name
And it ' s plausible that old stream slugs will have
be based on stream names that collide with modern
slugs :
4 - horseman
411
2016 - election
If there is any ambiguity about whether a stream slug
is old or modern , we prefer modern , as long as the integer
prefix matches a real stream id . Eventually we will
stop supporting the legacy slugs , which only matter now
because people have linked to Zulip threads in things like
GitHub conversations . We migrated to modern slugs in
early 2018.
* /
2021-02-10 14:31:52 +01:00
const m = /^(\d+)(-.*)?$/ . exec ( slug ) ;
2018-02-15 21:02:47 +01:00
if ( m ) {
2020-10-07 09:17:30 +02:00
const stream _id = Number . parseInt ( m [ 1 ] , 10 ) ;
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2018-02-15 21:02:47 +01:00
if ( sub ) {
return sub . name ;
}
// if nothing was found above, we try to match on the stream
// name in the somewhat unlikely event they had a historical
// link to a stream like 4-horsemen
}
2020-07-03 16:37:16 +02:00
/ *
We are dealing with a pre - 2018 slug that doesn ' t have the
stream id as a prefix .
* /
2018-02-15 21:02:47 +01:00
return slug ;
2021-02-28 00:53:59 +01:00
}
2018-02-15 21:02:47 +01:00
2021-02-28 00:53:59 +01:00
export function delete _sub ( stream _id ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2017-02-16 03:47:08 +01:00
if ( ! sub ) {
2021-03-31 16:15:24 +02:00
blueslip . warn ( "Failed to archive stream " + stream _id ) ;
2017-02-16 03:47:08 +01:00
return ;
}
2021-04-15 16:35:14 +02:00
sub _store . delete _sub ( stream _id ) ;
2020-02-03 07:41:38 +01:00
stream _info . delete ( sub . name ) ;
2021-02-28 00:53:59 +01:00
}
2013-08-21 23:21:31 +02:00
2021-02-28 00:53:59 +01:00
export function get _non _default _stream _names ( ) {
2020-02-04 23:46:56 +01:00
let subs = Array . from ( stream _info . values ( ) ) ;
2021-02-28 00:53:59 +01:00
subs = subs . filter ( ( sub ) => ! is _default _stream _id ( sub . stream _id ) && ! sub . invite _only ) ;
2020-07-02 01:39:34 +02:00
const names = subs . map ( ( sub ) => sub . name ) ;
2017-08-22 20:00:09 +02:00
return names ;
2021-02-28 00:53:59 +01:00
}
2017-08-22 20:00:09 +02:00
2021-02-28 00:53:59 +01:00
export function get _unsorted _subs ( ) {
2020-02-04 23:46:56 +01:00
return Array . from ( stream _info . values ( ) ) ;
2021-02-28 00:53:59 +01:00
}
2018-07-30 15:27:18 +02:00
2021-02-28 00:53:59 +01:00
export function num _subscribed _subs ( ) {
2019-12-27 15:56:46 +01:00
return stream _info . num _true _items ( ) ;
2021-02-28 00:53:59 +01:00
}
2019-12-27 15:56:46 +01:00
2021-02-28 00:53:59 +01:00
export function subscribed _subs ( ) {
2020-02-04 23:46:56 +01:00
return Array . from ( stream _info . true _values ( ) ) ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function unsubscribed _subs ( ) {
2020-02-04 23:46:56 +01:00
return Array . from ( stream _info . false _values ( ) ) ;
2021-02-28 00:53:59 +01:00
}
2016-10-25 21:45:19 +02:00
2021-02-28 00:53:59 +01:00
export function subscribed _streams ( ) {
return subscribed _subs ( ) . map ( ( sub ) => sub . name ) ;
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function subscribed _stream _ids ( ) {
return subscribed _subs ( ) . map ( ( sub ) => sub . stream _id ) ;
}
2020-07-25 18:20:54 +02:00
2021-06-17 22:26:17 +02:00
export function get _subscribed _streams _for _user ( user _id ) {
// Note that we only have access to subscribers of some streams
// depending on our role.
const all _subs = get _unsorted _subs ( ) ;
const subscribed _subs = [ ] ;
for ( const sub of all _subs ) {
if ( ! can _view _subscribers ( sub ) ) {
// Private streams that we have been removed from appear
// in get_unsorted_subs; we don't attempt to check their
// subscribers (which would trigger a warning).
continue ;
}
if ( is _user _subscribed ( sub . stream _id , user _id ) ) {
subscribed _subs . push ( sub ) ;
}
}
return subscribed _subs ;
}
2021-02-28 00:53:59 +01:00
export function get _invite _stream _data ( ) {
2020-03-22 17:31:47 +01:00
function get _data ( sub ) {
2019-01-10 17:57:35 +01:00
return {
name : sub . name ,
stream _id : sub . stream _id ,
invite _only : sub . invite _only ,
2020-03-22 17:04:47 +01:00
default _stream : default _stream _ids . has ( sub . stream _id ) ,
2019-01-10 17:57:35 +01:00
} ;
2020-03-22 17:31:47 +01:00
}
const streams = [ ] ;
// Invite users to all default streams...
for ( const stream _id of default _stream _ids ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2020-03-22 17:31:47 +01:00
streams . push ( get _data ( sub ) ) ;
}
// ...plus all your subscribed streams (avoiding repeats).
2021-02-28 00:53:59 +01:00
for ( const sub of subscribed _subs ( ) ) {
2020-03-22 17:31:47 +01:00
if ( ! default _stream _ids . has ( sub . stream _id ) ) {
streams . push ( get _data ( sub ) ) ;
2019-01-10 17:57:35 +01:00
}
2020-03-22 17:31:47 +01:00
}
2019-01-10 17:57:35 +01:00
return streams ;
2021-02-28 00:53:59 +01:00
}
2019-01-10 17:57:35 +01:00
2021-02-28 00:53:59 +01:00
export function get _colors ( ) {
return subscribed _subs ( ) . map ( ( sub ) => sub . color ) ;
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function update _stream _email _address ( sub , email ) {
2018-04-05 19:58:27 +02:00
sub . email _address = email ;
2021-02-28 00:53:59 +01:00
}
2018-04-05 19:58:27 +02:00
2021-02-28 00:53:59 +01:00
export function update _stream _post _policy ( sub , stream _post _policy ) {
2020-02-04 21:50:55 +01:00
sub . stream _post _policy = stream _post _policy ;
2021-02-28 00:53:59 +01:00
}
2019-05-07 07:12:14 +02:00
2021-02-28 00:53:59 +01:00
export function update _stream _privacy ( sub , values ) {
2019-05-07 07:12:14 +02:00
sub . invite _only = values . invite _only ;
sub . history _public _to _subscribers = values . history _public _to _subscribers ;
2020-11-10 15:57:14 +01:00
sub . is _web _public = values . is _web _public ;
2021-02-28 00:53:59 +01:00
}
2019-05-07 07:12:14 +02:00
2021-02-28 00:53:59 +01:00
export function update _message _retention _setting ( sub , message _retention _days ) {
2020-06-15 17:00:00 +02:00
sub . message _retention _days = message _retention _days ;
2021-02-28 00:53:59 +01:00
}
2020-06-15 17:00:00 +02:00
2021-02-28 00:53:59 +01:00
export function receives _notifications ( stream _id , notification _name ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2019-06-14 19:41:26 +02:00
if ( sub === undefined ) {
return false ;
}
if ( sub [ notification _name ] !== null ) {
return sub [ notification _name ] ;
}
2020-07-15 01:29:15 +02:00
if ( notification _name === "wildcard_mentions_notify" ) {
2021-07-28 16:00:58 +02:00
return user _settings [ notification _name ] ;
2019-11-26 02:37:12 +01:00
}
2021-07-28 16:00:58 +02:00
return user _settings [ "enable_stream_" + notification _name ] ;
2021-02-28 00:53:59 +01:00
}
2019-06-14 19:41:26 +02:00
2021-02-28 00:53:59 +01:00
export function all _subscribed _streams _are _in _home _view ( ) {
return subscribed _subs ( ) . every ( ( sub ) => ! sub . is _muted ) ;
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function home _view _stream _names ( ) {
const home _view _subs = subscribed _subs ( ) . filter ( ( sub ) => ! sub . is _muted ) ;
2020-07-02 01:39:34 +02:00
return home _view _subs . map ( ( sub ) => sub . name ) ;
2021-02-28 00:53:59 +01:00
}
2014-01-15 20:59:31 +01:00
2021-02-28 00:53:59 +01:00
export function canonicalized _name ( stream _name ) {
2018-05-06 21:43:17 +02:00
return stream _name . toString ( ) . toLowerCase ( ) ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function get _color ( stream _name ) {
const sub = get _sub ( stream _name ) ;
2013-08-15 21:11:07 +02:00
if ( sub === undefined ) {
2021-03-28 16:51:24 +02:00
return DEFAULT _COLOR ;
2013-08-15 21:11:07 +02:00
}
return sub . color ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function is _muted ( stream _id ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2019-05-21 09:33:21 +02:00
// Return true for undefined streams
if ( sub === undefined ) {
return true ;
}
return sub . is _muted ;
2021-02-28 00:53:59 +01:00
}
2017-05-13 20:54:53 +02:00
2021-02-28 00:53:59 +01:00
export function is _stream _muted _by _name ( stream _name ) {
const sub = get _sub ( stream _name ) ;
2019-05-21 09:33:21 +02:00
// Return true for undefined streams
if ( sub === undefined ) {
return true ;
}
return sub . is _muted ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-02-28 00:53:59 +01:00
export function is _notifications _stream _muted ( ) {
return is _muted ( page _params . realm _notifications _stream _id ) ;
}
2017-05-13 20:54:53 +02:00
2021-04-04 17:23:40 +02:00
export function can _toggle _subscription ( sub ) {
2021-11-25 02:43:43 +01:00
// You can always remove your subscription if you're subscribed.
//
// One can only join a stream if it is public (!invite_only) and
// your role is Member or above (!is_guest).
//
// Note that the correctness of this logic relies on the fact that
// one cannot be subscribed to a deactivated stream, and
// deactivated streams are automatically made private during the
// archive stream process.
2021-04-04 17:23:40 +02:00
return sub . subscribed || ( ! page _params . is _guest && ! sub . invite _only ) ;
}
2021-04-04 17:36:38 +02:00
export function can _preview ( sub ) {
return sub . subscribed || ! sub . invite _only || sub . previously _subscribed ;
}
2021-04-04 17:40:48 +02:00
export function can _change _permissions ( sub ) {
return page _params . is _admin && ( ! sub . invite _only || sub . subscribed ) ;
}
2021-04-04 17:52:09 +02:00
export function can _view _subscribers ( sub ) {
// Guest users can't access subscribers of any(public or private) non-subscribed streams.
return page _params . is _admin || sub . subscribed || ( ! page _params . is _guest && ! sub . invite _only ) ;
}
2021-04-04 18:15:17 +02:00
export function can _subscribe _others ( sub ) {
// User can add other users to stream if stream is public or user is subscribed to stream.
return ! page _params . is _guest && ( ! sub . invite _only || sub . subscribed ) ;
}
2021-11-23 00:37:45 +01:00
export function is _subscribed _by _name ( stream _name ) {
2021-02-28 00:53:59 +01:00
const sub = get _sub ( stream _name ) ;
2013-08-15 21:11:07 +02:00
return sub !== undefined && sub . subscribed ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-11-23 00:41:09 +01:00
export function is _subscribed ( stream _id ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2017-05-14 16:32:18 +02:00
return sub !== undefined && sub . subscribed ;
2021-02-28 00:53:59 +01:00
}
2017-05-14 16:32:18 +02:00
2021-02-28 00:53:59 +01:00
export function get _stream _privacy _policy ( stream _id ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2020-07-10 14:25:21 +02:00
2021-05-20 13:02:00 +02:00
if ( sub . is _web _public ) {
return stream _privacy _policy _values . web _public . code ;
}
2020-07-10 14:25:21 +02:00
if ( ! sub . invite _only ) {
2021-02-28 00:53:59 +01:00
return stream _privacy _policy _values . public . code ;
2020-07-10 14:25:21 +02:00
}
if ( sub . invite _only && ! sub . history _public _to _subscribers ) {
2021-02-28 00:53:59 +01:00
return stream _privacy _policy _values . private . code ;
2020-07-10 14:25:21 +02:00
}
2021-02-28 00:53:59 +01:00
return stream _privacy _policy _values . private _with _public _history . code ;
}
2020-07-10 14:25:21 +02:00
2021-05-20 13:02:00 +02:00
export function is _web _public ( stream _id ) {
const sub = sub _store . get ( stream _id ) ;
return sub !== undefined && sub . is _web _public ;
}
2021-11-23 00:30:21 +01:00
export function is _invite _only _by _stream _name ( stream _name ) {
2021-02-28 00:53:59 +01:00
const sub = get _sub ( stream _name ) ;
2013-08-15 21:11:07 +02:00
if ( sub === undefined ) {
return false ;
}
return sub . invite _only ;
2021-02-28 00:53:59 +01:00
}
2013-08-15 21:11:07 +02:00
2021-11-20 04:20:21 +01:00
export function is _web _public _by _stream _name ( stream _name ) {
const sub = get _sub ( stream _name ) ;
if ( sub === undefined ) {
return false ;
}
return sub . is _web _public ;
}
2021-02-28 00:53:59 +01:00
export function set _realm _default _streams ( realm _default _streams ) {
2017-08-22 18:20:00 +02:00
default _stream _ids . clear ( ) ;
2021-01-22 22:29:08 +01:00
for ( const stream of realm _default _streams ) {
2019-12-30 13:20:45 +01:00
default _stream _ids . add ( stream . stream _id ) ;
2021-01-22 22:29:08 +01:00
}
2021-02-28 00:53:59 +01:00
}
2017-08-22 18:20:00 +02:00
2021-02-28 00:53:59 +01:00
export function get _default _stream _ids ( ) {
2020-03-22 13:28:16 +01:00
return Array . from ( default _stream _ids ) ;
2021-02-28 00:53:59 +01:00
}
2018-07-22 11:30:38 +02:00
2021-02-28 00:53:59 +01:00
export function is _default _stream _id ( stream _id ) {
2017-08-22 18:20:00 +02:00
return default _stream _ids . has ( stream _id ) ;
2021-02-28 00:53:59 +01:00
}
2017-03-21 21:10:20 +01:00
2021-02-28 00:53:59 +01:00
export function get _name ( stream _name ) {
2013-08-19 19:25:44 +02:00
// This returns the actual name of a stream if we are subscribed to
// it (i.e "Denmark" vs. "denmark"), while falling thru to
// stream_name if we don't have a subscription. (Stream names
// are case-insensitive, but we try to display the actual name
// when we know it.)
2017-05-11 23:25:42 +02:00
//
// This function will also do the right thing if we have
// an old stream name in memory for a recently renamed stream.
2021-02-28 00:53:59 +01:00
const sub = get _sub _by _name ( stream _name ) ;
2013-08-19 19:25:44 +02:00
if ( sub === undefined ) {
return stream _name ;
}
return sub . name ;
2021-02-28 00:53:59 +01:00
}
2013-08-19 19:25:44 +02:00
2021-02-28 00:53:59 +01:00
export function maybe _get _stream _name ( stream _id ) {
2017-07-15 08:31:55 +02:00
if ( ! stream _id ) {
2020-09-24 07:50:36 +02:00
return undefined ;
2017-07-15 08:31:55 +02:00
}
2021-04-15 16:35:14 +02:00
const stream = sub _store . get ( stream _id ) ;
2017-07-15 08:31:55 +02:00
if ( ! stream ) {
2020-09-24 07:50:36 +02:00
return undefined ;
2017-07-15 08:31:55 +02:00
}
return stream . name ;
2021-02-28 00:53:59 +01:00
}
2017-07-15 08:31:55 +02:00
2021-02-28 00:53:59 +01:00
export function is _user _subscribed ( stream _id , user _id ) {
2021-04-15 16:35:14 +02:00
const sub = sub _store . get ( stream _id ) ;
2021-04-04 17:52:09 +02:00
if ( sub === undefined || ! can _view _subscribers ( sub ) ) {
2018-04-03 00:36:31 +02:00
// If we don't know about the stream, or we ourselves cannot access subscriber list,
2013-09-07 04:22:18 +02:00
// so we return undefined (treated as falsy if not explicitly handled).
2020-07-15 00:34:28 +02:00
blueslip . warn (
"We got a is_user_subscribed call for a non-existent or inaccessible stream." ,
) ;
2020-09-24 07:50:36 +02:00
return undefined ;
2013-09-07 02:48:44 +02:00
}
2021-03-24 20:14:12 +01:00
if ( user _id === undefined ) {
2018-04-06 05:22:07 +02:00
blueslip . warn ( "Undefined user_id passed to function is_user_subscribed" ) ;
2020-09-24 07:50:36 +02:00
return undefined ;
2016-10-30 15:47:20 +01:00
}
2021-01-12 21:38:01 +01:00
return peer _data . is _user _subscribed ( stream _id , user _id ) ;
2021-02-28 00:53:59 +01:00
}
2013-09-07 02:48:44 +02:00
2021-02-28 00:53:59 +01:00
export function create _streams ( streams ) {
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
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 { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
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);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.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;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
for ( const stream of streams ) {
2016-10-30 15:47:20 +01:00
// We handle subscriber stuff in other events.
2020-06-21 15:23:43 +02:00
2020-02-09 04:21:30 +01:00
const attrs = {
2016-10-30 15:47:20 +01:00
subscribers : [ ] ,
2017-01-12 00:17:43 +01:00
subscribed : false ,
2020-02-09 04:21:30 +01:00
... stream ,
} ;
2021-02-28 00:53:59 +01:00
create _sub _from _server _data ( attrs ) ;
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
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 { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
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);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.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;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
}
2021-02-28 00:53:59 +01:00
}
2016-10-15 21:10:10 +02:00
2021-04-04 19:07:25 +02:00
export function clean _up _description ( sub ) {
if ( sub . rendered _description !== undefined ) {
sub . rendered _description = sub . rendered _description . replace ( "<p>" , "" ) . replace ( "</p>" , "" ) ;
}
}
2021-02-28 00:53:59 +01:00
export function create _sub _from _server _data ( attrs ) {
2016-10-15 20:17:32 +02:00
if ( ! attrs . stream _id ) {
2020-09-24 07:56:29 +02:00
// fail fast
throw new Error ( "We cannot create a sub without a stream_id" ) ;
2016-10-15 20:17:32 +02:00
}
2021-04-15 16:35:14 +02:00
let sub = sub _store . get ( attrs . stream _id ) ;
2020-06-21 15:23:43 +02:00
if ( sub !== undefined ) {
// We've already created this subscription, no need to continue.
return sub ;
}
2016-10-15 20:17:32 +02:00
// Our internal data structure for subscriptions is mostly plain dictionaries,
// so we just reuse the attrs that are passed in to us, but we encapsulate how
2018-11-29 21:50:20 +01:00
// we handle subscribers. We defensively remove the `subscribers` field from
// the original `attrs` object, which will get thrown away. (We used to make
// a copy of the object with `_.omit(attrs, 'subscribers')`, but `_.omit` is
// slow enough to show up in timings when you have 1000s of streams.
2019-11-02 00:06:25 +01:00
const subscriber _user _ids = attrs . subscribers ;
2016-10-15 20:17:32 +02:00
2018-11-29 21:50:20 +01:00
delete attrs . subscribers ;
2020-02-09 04:21:30 +01:00
sub = {
2020-06-21 15:23:43 +02:00
name : attrs . name ,
2017-04-20 08:03:44 +02:00
render _subscribers : ! page _params . realm _is _zephyr _mirror _realm || attrs . invite _only === true ,
2016-10-15 20:17:32 +02:00
subscribed : true ,
2017-04-28 15:59:30 +02:00
newly _subscribed : false ,
2019-05-15 08:54:25 +02:00
is _muted : false ,
2016-10-15 20:17:32 +02:00
invite _only : false ,
2021-10-08 13:51:55 +02:00
desktop _notifications : null ,
audible _notifications : null ,
push _notifications : null ,
email _notifications : null ,
wildcard _mentions _notify : null ,
2020-07-15 01:29:15 +02:00
description : "" ,
rendered _description : "" ,
2019-03-13 13:47:57 +01:00
first _message _id : attrs . first _message _id ,
2020-02-09 04:21:30 +01:00
... attrs ,
} ;
2016-10-15 20:17:32 +02:00
2021-01-13 22:03:25 +01:00
peer _data . set _subscribers ( sub . stream _id , subscriber _user _ids ) ;
2016-10-15 20:17:32 +02:00
if ( ! sub . color ) {
2018-11-28 23:12:40 +01:00
sub . color = color _data . pick _color ( ) ;
2016-10-15 20:17:32 +02:00
}
2021-04-04 19:07:25 +02:00
clean _up _description ( sub ) ;
2018-03-20 22:28:57 +01:00
2021-01-12 20:58:11 +01:00
stream _info . set ( sub . name , sub ) ;
2021-04-15 16:35:14 +02:00
sub _store . add _hydrated _sub ( sub . stream _id , sub ) ;
2016-10-15 20:17:32 +02:00
return sub ;
2021-02-28 00:53:59 +01:00
}
2016-10-15 20:17:32 +02:00
2021-02-28 00:53:59 +01:00
export function get _streams _for _admin ( ) {
2017-08-22 20:51:41 +02:00
// Sort and combine all our streams.
2018-12-07 21:21:39 +01:00
function by _name ( a , b ) {
2017-08-22 20:51:41 +02:00
return util . strcmp ( a . name , b . name ) ;
}
2020-02-04 23:46:56 +01:00
const subs = Array . from ( stream _info . values ( ) ) ;
2017-08-22 20:51:41 +02:00
subs . sort ( by _name ) ;
return subs ;
2021-02-28 00:53:59 +01:00
}
2017-08-22 20:51:41 +02:00
2020-04-15 18:29:26 +02:00
/ *
This module provides a common helper for finding the notification
stream , but we don ' t own the data . The ` page_params ` structure
is the authoritative source of this data , and it will be updated by
server _events _dispatch in case of changes .
* /
2021-02-28 00:53:59 +01:00
export function realm _has _notifications _stream ( ) {
return page _params . realm _notifications _stream _id !== - 1 ;
}
2020-04-15 18:29:26 +02:00
2021-02-28 00:53:59 +01:00
export function get _notifications _stream ( ) {
2020-04-14 12:55:18 +02:00
const stream _id = page _params . realm _notifications _stream _id ;
if ( stream _id !== - 1 ) {
2021-04-15 16:35:14 +02:00
const stream _obj = sub _store . get ( stream _id ) ;
2020-04-14 12:55:18 +02:00
if ( stream _obj ) {
return stream _obj . name ;
}
// We reach here when the notifications stream is a private
// stream the current user is not subscribed to.
}
2020-07-15 01:29:15 +02:00
return "" ;
2021-02-28 00:53:59 +01:00
}
2020-04-14 12:55:18 +02:00
2021-02-28 00:53:59 +01:00
export function initialize ( params ) {
2020-02-25 12:16:26 +01:00
/ *
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 ;
2020-03-22 17:32:31 +01:00
const realm _default _streams = params . realm _default _streams ;
2020-02-25 12:16:26 +01:00
/ *
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
2020-03-22 17:32:31 +01:00
be fully handled by stream _data .
2020-02-25 12:16:26 +01:00
* /
color _data . claim _colors ( subscriptions ) ;
2018-11-28 23:12:40 +01:00
2018-02-06 09:42:41 +01:00
function populate _subscriptions ( subs , subscribed , previously _subscribed ) {
2021-01-22 22:29:08 +01:00
for ( const sub of subs ) {
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
sub . subscribed = subscribed ;
2018-02-06 09:42:41 +01:00
sub . previously _subscribed = previously _subscribed ;
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
2021-02-28 00:53:59 +01:00
create _sub _from _server _data ( sub ) ;
2021-01-22 22:29:08 +01:00
}
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
}
2021-02-28 00:53:59 +01:00
set _realm _default _streams ( realm _default _streams ) ;
2017-03-21 21:10:20 +01:00
2020-02-25 12:16:26 +01:00
populate _subscriptions ( subscriptions , true , true ) ;
populate _subscriptions ( unsubscribed , false , true ) ;
populate _subscriptions ( never _subscribed , false , false ) ;
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
2021-02-28 00:53:59 +01:00
set _filter _out _inactives ( ) ;
}
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
2021-02-28 00:53:59 +01:00
export function remove _default _stream ( stream _id ) {
2019-12-30 13:20:45 +01:00
default _stream _ids . delete ( stream _id ) ;
2021-02-28 00:53:59 +01:00
}