Skip to content

Commit fbea2a2

Browse files
committed
flow: Use a Flow enum for the first time! (CreateWebPublicStreamPolicy)
This is nice and simple, particularly in the definition. We remove the `default` case in the two `switch`es on a CreateWebPublicStreamPolicy value, and in particular that means removing the `ensureUnreachable`s there. But its job will still be done: Flow still tracks whether the cases are exhaustive and will complain if not.
1 parent d431b78 commit fbea2a2

File tree

7 files changed

+38
-79
lines changed

7 files changed

+38
-79
lines changed

src/__tests__/permissionSelectors-test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
CreatePublicOrPrivateStreamPolicy,
1010
type CreatePublicOrPrivateStreamPolicyT,
1111
CreateWebPublicStreamPolicy,
12-
type CreateWebPublicStreamPolicyT,
1312
} from '../api/permissionsTypes';
1413
import {
1514
getHasUserPassedWaitingPeriod,
@@ -301,7 +300,7 @@ describe('getCanCreateWebPublicStreams', () => {
301300
role,
302301
expected,
303302
}: {
304-
policy: CreateWebPublicStreamPolicyT,
303+
policy: CreateWebPublicStreamPolicy,
305304
role: RoleT,
306305
expected: boolean,
307306
}) => {

src/api/initialDataTypes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type {
1818
} from './apiTypes';
1919
import type {
2020
CreatePublicOrPrivateStreamPolicyT,
21-
CreateWebPublicStreamPolicyT,
21+
CreateWebPublicStreamPolicy,
2222
} from './permissionsTypes';
2323

2424
/*
@@ -176,7 +176,7 @@ export type InitialDataRealm = $ReadOnly<{|
176176

177177
// TODO(server-5.0): Added in feat. 103; when absent, treat as
178178
// CreateWebPublicStreamPolicy.Nobody.
179-
realm_create_web_public_stream_policy?: CreateWebPublicStreamPolicyT,
179+
realm_create_web_public_stream_policy?: CreateWebPublicStreamPolicy,
180180

181181
realm_default_code_block_language: string | null,
182182

src/api/permissionsTypes.js

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const RoleValues: $ReadOnlyArray<RoleT> = objectValues(Role);
4747
* (These policies could be set differently, but their types are the same,
4848
* so it's convenient to have just one definition.)
4949
*
50-
* For the policy for web-public streams, see CreateWebPublicStreamPolicyT.
50+
* For the policy for web-public streams, see CreateWebPublicStreamPolicy.
5151
*/
5252
// eslint-disable-next-line ft-flow/type-id-match
5353
export type CreatePublicOrPrivateStreamPolicyT = 1 | 2 | 3 | 4;
@@ -89,33 +89,9 @@ export const CreatePublicOrPrivateStreamPolicyValues: $ReadOnlyArray<CreatePubli
8989
* organization. Ignore if the server hasn't opted into the concept of
9090
* web-public streams.
9191
*/
92-
// Ideally both the type and enum would have the simple name; but a type
93-
// and value sharing a name is one nice TS feature that Flow lacks.
94-
// Or we could leapfrog TS and make this a Flow enum:
95-
// https://flow.org/en/docs/enums/
96-
// (TS has its own enums, but they are a mess.)
97-
// eslint-disable-next-line ft-flow/type-id-match
98-
export type CreateWebPublicStreamPolicyT = 2 | 4 | 6 | 7;
99-
100-
/**
101-
* An enum of all valid values for `CreateWebPublicStreamPolicyT`.
102-
*
103-
* See CreateWebPublicStreamPolicyValues for the list of values.
104-
*/
105-
export const CreateWebPublicStreamPolicy = {
106-
AdminOrAbove: (2: 2),
107-
ModeratorOrAbove: (4: 4),
108-
Nobody: (6: 6),
109-
OwnerOnly: (7: 7),
110-
};
111-
112-
// Check that the enum indeed has all and exactly the values of the type.
113-
typesEquivalent<CreateWebPublicStreamPolicyT, $Values<typeof CreateWebPublicStreamPolicy>>();
114-
115-
/**
116-
* A list of all valid values for `CreateWebPublicStreamPolicyT`.
117-
*
118-
* See CreateWebPublicStreamPolicy for an enum to refer to these by meaningful names.
119-
*/
120-
export const CreateWebPublicStreamPolicyValues: $ReadOnlyArray<CreateWebPublicStreamPolicyT> =
121-
objectValues(CreateWebPublicStreamPolicy);
92+
export enum CreateWebPublicStreamPolicy {
93+
AdminOrAbove = 2,
94+
ModeratorOrAbove = 4,
95+
Nobody = 6,
96+
OwnerOnly = 7,
97+
}

src/permissionSelectors.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* @flow strict-local */
22
import type { PerAccountState, UserId } from './types';
33
import { ensureUnreachable } from './types';
4-
import { Role, type RoleT } from './api/permissionsTypes';
4+
import { Role, type RoleT, CreateWebPublicStreamPolicy } from './api/permissionsTypes';
55
import { getRealm, getOwnUser, getUserForId } from './selectors';
66

77
const { Guest, Member, Moderator, Admin, Owner } = Role;
@@ -164,25 +164,13 @@ export function getCanCreateWebPublicStreams(state: PerAccountState): boolean {
164164
}
165165

166166
switch (createWebPublicStreamPolicy) {
167-
// FlowIssue: sad that we end up having to write numeric literals here :-/
168-
// But the most important thing to get from the type-checker here is
169-
// that the ensureUnreachable works -- that ensures that when we add a
170-
// new possible value, we'll add a case for it here. Couldn't find a
171-
// cleaner way to write this that still accomplished that. Discussion:
172-
// https://github.com/zulip/zulip-mobile/pull/5384#discussion_r875147220
173-
case 6: // CreateWebPublicStreamPolicy.Nobody
167+
case CreateWebPublicStreamPolicy.Nobody:
174168
return false;
175-
case 7: // CreateWebPublicStreamPolicy.OwnerOnly
169+
case CreateWebPublicStreamPolicy.OwnerOnly:
176170
return roleIsAtLeast(role, Owner);
177-
case 2: // CreateWebPublicStreamPolicy.AdminOrAbove
171+
case CreateWebPublicStreamPolicy.AdminOrAbove:
178172
return roleIsAtLeast(role, Admin);
179-
case 4: // CreateWebPublicStreamPolicy.ModeratorOrAbove
173+
case CreateWebPublicStreamPolicy.ModeratorOrAbove:
180174
return roleIsAtLeast(role, Moderator);
181-
default: {
182-
ensureUnreachable(createWebPublicStreamPolicy);
183-
184-
// (Unreachable as long as the cases are exhaustive.)
185-
return false;
186-
}
187175
}
188176
}

src/realm/__tests__/realmReducer-test.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ import { CustomProfileFieldType } from '../../api/modelTypes';
2020
import { EventTypes } from '../../api/eventTypes';
2121
import * as eg from '../../__tests__/lib/exampleData';
2222

23+
const labelFromValue = value =>
24+
/* $FlowFixMe[incompatible-use] - CreateWebPublicStreamPolicy is a
25+
Flow enum. Values are numbers, so they do have .toString…but
26+
Flow reasonably hides that detail from consumers. For
27+
CreateWebPublicStreamPolicy, it would actually be better to use
28+
CreateWebPublicStreamPolicy.getName(initialStateValue), if we
29+
find a nice way to write that with type checking. */
30+
value?.toString() ?? '[nullish]';
31+
2332
describe('realmReducer', () => {
2433
describe('REGISTER_COMPLETE', () => {
2534
test('updates as appropriate on a boring but representative REGISTER_COMPLETE', () => {
@@ -289,8 +298,7 @@ describe('realmReducer', () => {
289298
eventPropertyName: E,
290299
): ((RealmState[S], UserSettings[E]) => void) =>
291300
(initialStateValue, eventValue) => {
292-
/* prettier-ignore */ // (wants to wrap the name weirdly)
293-
test(`${initialStateValue?.toString() ?? '[nullish]'}${eventValue?.toString() ?? '[nullish]'}`, () => {
301+
test(`${labelFromValue(initialStateValue)}${labelFromValue(eventValue)}`, () => {
294302
const initialState = { ...eg.plusReduxState.realm };
295303
// $FlowFixMe[prop-missing]
296304
// $FlowFixMe[class-object-subtyping]
@@ -331,8 +339,7 @@ describe('realmReducer', () => {
331339
eventPropertyName: E,
332340
): ((RealmState[S], RealmDataForUpdate[E]) => void) =>
333341
(initialStateValue, eventValue) => {
334-
/* prettier-ignore */ // (wants to wrap the name weirdly)
335-
test(`${initialStateValue?.toString() ?? '[nullish]'}${eventValue?.toString() ?? '[nullish]'}`, () => {
342+
test(`${labelFromValue(initialStateValue)}${labelFromValue(eventValue)}`, () => {
336343
const initialState = { ...eg.plusReduxState.realm };
337344
// $FlowFixMe[prop-missing]
338345
// $FlowFixMe[class-object-subtyping]

src/reduxTypes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {
2626
UserId,
2727
UserPresence,
2828
CreatePublicOrPrivateStreamPolicyT,
29-
CreateWebPublicStreamPolicyT,
29+
CreateWebPublicStreamPolicy,
3030
} from './api/apiTypes';
3131
import type {
3232
PerAccountSessionState,
@@ -302,7 +302,7 @@ export type RealmState = {|
302302
+createPublicStreamPolicy: CreatePublicOrPrivateStreamPolicyT,
303303
+createPrivateStreamPolicy: CreatePublicOrPrivateStreamPolicyT,
304304
+webPublicStreamsEnabled: boolean,
305-
+createWebPublicStreamPolicy: CreateWebPublicStreamPolicyT,
305+
+createWebPublicStreamPolicy: CreateWebPublicStreamPolicy,
306306
+enableSpectatorAccess: boolean,
307307
+waitingPeriodThreshold: number,
308308
+allowEditHistory: boolean,

src/streams/EditStreamCard.js

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import type { Privacy } from './streamsActions';
77
import { ensureUnreachable } from '../types';
88
import { useSelector } from '../react-redux';
99
import { getRealm, getRealmUrl } from '../selectors';
10-
import { Role } from '../api/permissionsTypes';
10+
import {
11+
Role,
12+
type CreatePublicOrPrivateStreamPolicyT,
13+
CreateWebPublicStreamPolicy,
14+
} from '../api/permissionsTypes';
1115
import {
1216
getCanCreatePublicStreams,
1317
getCanCreatePrivateStreams,
@@ -22,10 +26,6 @@ import ZulipTextIntl from '../common/ZulipTextIntl';
2226
import ZulipButton from '../common/ZulipButton';
2327
import styles from '../styles';
2428
import { TranslationContext } from '../boot/TranslationProvider';
25-
import type {
26-
CreatePublicOrPrivateStreamPolicyT,
27-
CreateWebPublicStreamPolicyT,
28-
} from '../api/permissionsTypes';
2929
import type { LocalizableText } from '../types';
3030
import { showConfirmationDialog } from '../utils/info';
3131

@@ -64,31 +64,20 @@ type PropsCreateStream = $ReadOnly<{|
6464
type Props = $ReadOnly<PropsEditStream | PropsCreateStream>;
6565

6666
function explainCreateWebPublicStreamPolicy(
67-
policy: CreateWebPublicStreamPolicyT,
67+
policy: CreateWebPublicStreamPolicy,
6868
realmName: string,
6969
): LocalizableText {
7070
return {
7171
text: (() => {
7272
switch (policy) {
73-
// FlowIssue: sad that we end up having to write numeric literals here :-/
74-
// But the most important thing to get from the type-checker here is
75-
// that the ensureUnreachable works -- that ensures that when we add a
76-
// new possible value, we'll add a case for it here. Couldn't find a
77-
// cleaner way to write this that still accomplished that. Discussion:
78-
// https://github.com/zulip/zulip-mobile/pull/5384#discussion_r875147220
79-
case 6: // CreateWebPublicStreamPolicy.Nobody
73+
case CreateWebPublicStreamPolicy.Nobody:
8074
return '{realmName} does not allow anybody to make web-public streams.';
81-
case 7: // CreateWebPublicStreamPolicy.OwnerOnly
75+
case CreateWebPublicStreamPolicy.OwnerOnly:
8276
return '{realmName} only allows organization owners to make web-public streams.';
83-
case 2: // CreateWebPublicStreamPolicy.AdminOrAbove
77+
case CreateWebPublicStreamPolicy.AdminOrAbove:
8478
return '{realmName} only allows organization administrators or owners to make web-public streams.';
85-
case 4: // CreateWebPublicStreamPolicy.ModeratorOrAbove
79+
case CreateWebPublicStreamPolicy.ModeratorOrAbove:
8680
return '{realmName} only allows organization moderators, administrators, or owners to make web-public streams.';
87-
default: {
88-
ensureUnreachable(policy);
89-
// (Unreachable as long as the cases are exhaustive.)
90-
return '';
91-
}
9281
}
9382
})(),
9483
values: { realmName },

0 commit comments

Comments
 (0)