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" ;
2023-05-08 09:01:27 +02:00
import * as settings _data from "./settings_data" ;
2021-04-15 16:35:14 +02:00
import * as sub _store from "./sub_store" ;
2022-12-29 17:49:38 +01:00
import * as user _groups from "./user_groups" ;
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 ;
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 = {
2021-12-07 07:57:29 +01:00
web _public : {
code : "web-public" ,
2022-01-29 00:54:13 +01:00
name : $t ( { defaultMessage : "Web-public" } ) ,
2021-12-07 07:57:29 +01:00
description : $t ( {
defaultMessage :
"Organization members can join (guests must be invited by a subscriber); anyone on the Internet can view complete message history without creating an account" ,
} ) ,
} ,
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
} ;
2021-05-20 13:02:00 +02:00
2021-02-28 00:53:59 +01:00
export const stream _post _policy _values = {
2021-12-15 01:04:35 +01:00
// These strings should match the strings in the
// Stream.POST_POLICIES object in zerver/models.py.
2020-02-04 21:50:55 +01:00
everyone : {
code : 1 ,
2022-03-07 07:29:06 +01:00
description : $t ( { defaultMessage : "Everyone" } ) ,
2020-02-04 21:50:55 +01:00
} ,
2022-03-07 07:29:06 +01:00
non _new _members : {
code : 3 ,
description : $t ( { defaultMessage : "Admins, moderators and full members" } ) ,
2020-02-04 21:50:55 +01:00
} ,
2021-05-01 16:19:33 +02:00
moderators : {
code : 4 ,
description : $t ( {
2022-03-07 07:29:06 +01:00
defaultMessage : "Admins and moderators" ,
2021-05-01 16:19:33 +02:00
} ) ,
} ,
2022-03-07 07:29:06 +01:00
admins : {
code : 2 ,
description : $t ( { defaultMessage : "Admins only" } ) ,
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 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 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.
2022-02-28 20:54:06 +01:00
name = name . replaceAll ( " " , "-" ) ;
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 ( ) {
2023-03-02 01:58:25 +01:00
let subs = [ ... 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 ) ;
2022-04-01 19:22:41 +02:00
const names = subs . map ( ( sub ) => ( {
name : sub . name ,
unique _id : sub . stream _id . toString ( ) ,
} ) ) ;
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 ( ) {
2023-03-02 01:58:25 +01:00
return [ ... 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 ( ) {
2023-03-02 01:58:25 +01:00
return [ ... 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 ( ) {
2023-03-02 01:58:25 +01:00
return [ ... 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-11-20 11:51:35 +01:00
export function muted _stream _ids ( ) {
return subscribed _subs ( )
. filter ( ( sub ) => sub . is _muted )
. map ( ( sub ) => sub . stream _id ) ;
}
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 ,
2022-12-15 06:03:19 +01:00
is _web _public : sub . is _web _public ,
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
2022-12-29 18:36:06 +01:00
export function update _can _remove _subscribers _group _id ( sub , can _remove _subscribers _group _id ) {
sub . can _remove _subscribers _group _id = can _remove _subscribers _group _id ;
}
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 ] ;
}
2022-08-25 21:09:32 +02:00
return user _settings [ settings _config . generalize _stream _notification _setting [ 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).
2021-11-16 07:05:04 +01:00
// Spectators cannot subscribe to any streams.
2021-11-25 02:43:43 +01:00
//
// 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-11-16 07:05:04 +01:00
return (
( sub . subscribed || ( ! page _params . is _guest && ! sub . invite _only ) ) && ! page _params . is _spectator
) ;
2021-04-04 17:23:40 +02:00
}
2022-10-18 19:01:58 +02:00
export function can _access _topic _history ( sub ) {
// Anyone can access topic history for web-public streams and
// subscriptions; additionally, members can access history for
// public streams.
return sub . is _web _public || can _toggle _subscription ( sub ) ;
}
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 ) {
2023-05-08 09:01:27 +02:00
// User can add other users to stream if stream is public or user is subscribed to stream
// and realm level setting allows user to add subscribers.
return (
! page _params . is _guest &&
( ! sub . invite _only || sub . subscribed ) &&
settings _data . user _can _subscribe _other _users ( )
) ;
2021-04-04 18:15:17 +02:00
}
2022-12-29 17:49:38 +01:00
export function can _unsubscribe _others ( sub ) {
// Whether the current user has permission to remove other users
// from the stream. Organization administrators can remove users
// from any stream; additionally, users who can access the stream
// and are in the stream's can_remove_subscribers_group can do so
// as well.
//
// TODO: The API allows the current user to remove bots that it
// administers from streams; so we might need to refactor this
// logic to accept a target_user_id parameter in order to support
// that in the UI.
// A user must be able to view subscribers in a stream in order to
// remove them. This check may never fire in practice, since the
// UI for removing subscribers generally is a list of the stream's
// subscribers.
if ( ! can _view _subscribers ( sub ) ) {
return false ;
}
if ( page _params . is _admin ) {
return true ;
}
return user _groups . is _user _in _group (
sub . can _remove _subscribers _group _id ,
people . my _current _user _id ( ) ,
) ;
}
2021-12-27 10:27:03 +01:00
export function can _post _messages _in _stream ( stream ) {
2022-11-10 01:45:15 +01:00
if ( page _params . is _spectator ) {
return false ;
}
2021-12-27 10:27:03 +01:00
if ( page _params . is _admin ) {
return true ;
}
if ( stream . stream _post _policy === stream _post _policy _values . admins . code ) {
return false ;
}
if ( page _params . is _moderator ) {
return true ;
}
if ( stream . stream _post _policy === stream _post _policy _values . moderators . code ) {
return false ;
}
if (
page _params . is _guest &&
stream . stream _post _policy !== stream _post _policy _values . everyone . code
) {
return false ;
}
const person = people . get _by _user _id ( people . my _current _user _id ( ) ) ;
const current _datetime = new Date ( Date . now ( ) ) ;
const person _date _joined = new Date ( person . date _joined ) ;
const days = ( current _datetime - person _date _joined ) / 1000 / 86400 ;
if (
stream . stream _post _policy === stream _post _policy _values . non _new _members . code &&
days < page _params . realm _waiting _period _threshold
) {
return false ;
}
return true ;
}
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 ( ) {
2023-03-02 01:58:25 +01:00
return [ ... 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 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 = {
2023-03-22 10:10:58 +01:00
stream _weekly _traffic : null ,
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 ) ;
}
2023-03-02 01:58:25 +01:00
const subs = [ ... 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 ) ;
2021-02-28 00:53:59 +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
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
}
2023-06-20 11:30:30 +02:00
export function get _options _for _dropdown _widget ( ) {
return (
subscribed _subs ( )
. map ( ( stream ) => ( {
name : stream . name ,
unique _id : stream . stream _id ,
stream ,
} ) )
// eslint-disable-next-line consistent-return, array-callback-return
. sort ( ( a , b ) => {
if ( a . name . toLowerCase ( ) < b . name . toLowerCase ( ) ) {
return - 1 ;
}
if ( a . name . toLowerCase ( ) > b . name . toLowerCase ( ) ) {
return 1 ;
}
// Not possible since two streams cannot have same name.
// return 0;
} )
) ;
}