-
-
Notifications
You must be signed in to change notification settings - Fork 654
/
modelTypes.js
1127 lines (986 loc) · 35.6 KB
/
modelTypes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Types for things in the Zulip data model, as seen in the API.
*
* @flow strict-local
*/
import { typesEquivalent } from '../generics';
import { objectValues } from '../flowPonyfill';
import type { AvatarURL } from '../utils/avatar';
import type { UserId } from './idTypes';
import type { Role } from './permissionsTypes';
export type * from './idTypes';
//
//
//
// ===================================================================
// Data attached to the realm or the server.
//
//
/**
* See CustomProfileField for semantics.
*
* See also CustomProfileFieldType for an enum with meaningful names, and
* CustomProfileFieldTypeValues for a list of values.
*/
// This is an enum; see discussion on other enums.
// eslint-disable-next-line ft-flow/type-id-match
export type CustomProfileFieldTypeT = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
/**
* An enum of all valid values for CustomProfileFieldTypeT.
*
* See CustomProfileFieldTypeValues for a list of values,
* and CustomProfileField for semantics.
*/
export const CustomProfileFieldType = {
// The names are from a mix of the descriptions on custom_profile_fields
// itself, and the names in custom_profile_field_types:
// https://zulip.com/api/register-queue
ShortText: (1: 1),
LongText: (2: 2),
Choice: (3: 3),
Date: (4: 4),
Link: (5: 5),
User: (6: 6),
ExternalAccount: (7: 7),
Pronouns: (8: 8),
};
// Check that the enum indeed has all and only the values of the type.
typesEquivalent<CustomProfileFieldTypeT, $Values<typeof CustomProfileFieldType>>();
/**
* A list of all valid values for CustomProfileFieldTypeT.
*
* See CustomProfileFieldType for an enum to refer to these by meaningful
* names.
*/
export const CustomProfileFieldTypeValues: $ReadOnlyArray<CustomProfileFieldTypeT> =
objectValues(CustomProfileFieldType);
/**
* A custom profile field available to users in this org.
*
* See event doc: https://zulip.com/api/get-events#custom_profile_fields
*/
export type CustomProfileField = {|
+id: number,
+type: CustomProfileFieldTypeT,
+order: number,
+name: string,
+hint: string,
/**
* Some data in a format determined by the custom profile field's `type`.
*
* Empirically this is an empty string for field types that don't use it.
*
* Docs wrongly say it's a "JSON dictionary" for type 3; the example shows
* it's a string, one serializing a JSON object.
*/
+field_data: string,
+display_in_profile_summary?: true,
|};
export type RealmEmoji = $ReadOnly<{|
author?: $ReadOnly<{|
email: string,
full_name: string,
id: number,
|}>,
deactivated: boolean,
id?: number,
code: string,
name: string,
source_url: string,
|}>;
export type RealmEmojiById = $ReadOnly<{|
[id: string]: RealmEmoji,
|}>;
export type ImageEmoji = {|
...RealmEmoji,
+reaction_type: ReactionType, // eslint-disable-line no-use-before-define
|};
export type ImageEmojiById = $ReadOnly<{|
[id: string]: ImageEmoji,
|}>;
/**
* The server's record of available Unicode emojis, with primary names and
* aliases.
*
* See `server_emoji_data_url` at https://zulip.com/api/register-queue.
*/
export type ServerEmojiData = {|
+code_to_names: Map<
// `emoji_code` for an available Unicode emoji: a "dash-separated hex
// encoding of the sequence of Unicode codepoints that define this emoji
// in the Unicode specification."
string,
// `emoji_name`s for the Unicode emoji. The canonical name appears
// first, followed by any aliases.
$ReadOnlyArray<string>,
>,
|};
/**
* The only way servers before feature level 54 represent linkifiers.
*/
// TODO(server-4.0): Delete this.
export type RealmFilter = [string, string, number];
/**
* The way servers at feature level 54+ can represent linkifiers.
*
* Currently, this new format can be converted to the old without loss of
* information, so we do that at the edge and continue to represent the data
* internally with the old format.
*/
// TODO:
// - When we've moved to a shared markdown implementation (#4242),
// change our internal representation to be the new
// `realm_linkifiers` format. (When doing so, also don't forget to
// change various variable and type definition names to be like
// `realm_linkifiers`.)
// - TODO(server-4.0): When we drop support for servers older than 54, we
// can remove all our code that knows about the `realm_filters` format.
export type RealmLinkifier = $ReadOnly<{|
id: number,
pattern: string,
url_format: string,
|}>;
//
//
//
// ===================================================================
// Data attached to the current user on the realm.
//
//
/**
* The user settings, stored on the server.
*
* See:
* - PATCH /settings doc: https://zulip.com/api/update-settings
* - POST /register doc: https://zulip.com/api/register-queue
*/
// Assumes FL 89+, because servers added user_settings to the /register
// response in FL 89.
//
// Current to FL 152; property ordering follows the /register doc. See the
// "Current to" notes in the two places we use this, and update them as
// needed:
// - InitialDataUserSettings (for POST /register response)
// - api.updateUserSettings (for the PATCH /settings endpoint)
//
// TODO(server-5.0): Remove FL 89+ comment.
export type UserSettings = {|
+twenty_four_hour_time: boolean,
+dense_mode: boolean,
+starred_message_counts: boolean,
+fluid_layout_width: boolean,
+high_contrast_mode: boolean,
+color_scheme: 1 | 2 | 3,
+translate_emoticons: boolean,
// TODO(server-6.0): New in FL 125.
+display_emoji_reaction_users?: boolean,
+default_language: string,
+default_view: 'recent_topics' | 'all_messages',
// TODO(server-5.0): New in FL 107.
+escape_navigates_to_default_view?: boolean,
+left_side_userlist: boolean,
+emojiset: 'google' | 'google-blob' | 'twitter' | 'text',
+demote_inactive_streams: 1 | 2 | 3,
// TODO(server-6.0): New in FL 141.
+user_list_style: 1 | 2 | 3,
+timezone: string,
+enter_sends: boolean,
+enable_drafts_synchronization: boolean,
+enable_stream_desktop_notifications: boolean,
+enable_stream_email_notifications: boolean,
+enable_stream_push_notifications: boolean,
+enable_stream_audible_notifications: boolean,
+notification_sound: string,
+enable_desktop_notifications: boolean,
+enable_sounds: boolean,
+email_notifications_batching_period_seconds: number,
+enable_offline_email_notifications: boolean,
+enable_offline_push_notifications: boolean,
+enable_online_push_notifications: boolean,
+enable_digest_emails: boolean,
+enable_marketing_emails: boolean,
+enable_login_emails: boolean,
+message_content_in_email_notifications: boolean,
+pm_content_in_desktop_notifications: boolean,
+wildcard_mentions_notify: boolean,
+desktop_icon_count_display: 1 | 2 | 3,
+realm_name_in_notifications: boolean,
+presence_enabled: boolean,
// TODO(server-5.0): New in FL 105.
+send_private_typing_notifications?: boolean,
// TODO(server-5.0): New in FL 105.
+send_stream_typing_notifications?: boolean,
// TODO(server-5.0): New in FL 105.
+send_read_receipts?: boolean,
|};
//
//
//
// ===================================================================
// Users and data about them.
//
//
export type DevUser = $ReadOnly<{|
realm_uri: string,
email: string,
|}>;
/**
* A Zulip user, which might be a human or a bot, as found in one realm.
*
* This is a user object as found in properties `realm_users` and
* `realm_non_active_users` of a `/register` response. See the API doc:
* https://zulip.com/api/register-queue
*
* See also:
* * `CrossRealmBot` for another type of bot user, found in a
* different part of a `/register` response
* * `UserOrBot` for a convenience union of the two
*/
export type User = {|
// Property ordering follows the doc.
// Current to feature level (FL) 121.
+user_id: UserId,
// Currently `delivery_email` is always `string` if present. A planned
// future API may make it sometimes `null`, to explicitly indicate that no
// delivery email for this user is visible to us:
// https://chat.zulip.org/#narrow/stream/378-api-design/topic/email.20address.20visibility/near/1296132
+delivery_email?: string | null,
+email: string,
+full_name: string,
// We expect ISO 8601; that's in the doc's example response.
+date_joined: string,
// is_active never appears in `/register` responses, at least up through
// FL 121. The doc wrongly says it always appears. See
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/.60is_active.60.20in.20.60.2Fregister.60.20response/near/1371606
// TODO(server-5.0): New in FL 73
+is_billing_admin?: boolean,
// For background on the "*bot*" fields, see user docs on bots:
// https://zulip.com/help/add-a-bot-or-integration
// Note that certain bots are represented by a different type entirely,
// namely `CrossRealmBot`.
+is_bot: boolean,
+bot_type: number | null,
// TODO(server-3.0): New in FL 1, replacing bot_owner
+bot_owner_id?: number | null,
// TODO(server-3.0): Replaced in FL 1 by bot_owner_id
+bot_owner?: string,
+role: Role,
// The ? is for future-proofing. Greg explains in 2020-02, at
// https://github.com/zulip/zulip-mobile/pull/3789#discussion_r378554698 ,
// that both human and bot Users will likely end up having a missing
// timezone instead of an empty string.
+timezone?: string,
/**
* Present under EVENT_USER_ADD, RealmUserUpdateEvent (if change
* indicated), under REGISTER_COMPLETE, and in `state.users`, all as an
* AvatarURL, because we translate into that form at the edge.
*
* For how it appears at the edge (and how we translate) see
* AvatarURL.fromUserOrBotData.
*/
+avatar_url: AvatarURL,
// If we use this, avoid `avatar_url` falling out of sync with it.
-avatar_version: number,
+profile_data?: {|
+[id: string]: {|
+value: string,
// New in server 2.0, server commit e3aed0f7b.
// TODO(server-2.0): Delete the server-2.0 comment, but keep the type
// optional; only some custom profile field types support Markdown.
+rendered_value?: string,
|},
|},
|};
/**
* A "cross-realm bot", a bot user shared across the realms on a Zulip server.
*
* Cross-realm bots are used for a handful of bots defined in the Zulip
* server code, like Welcome Bot. They're found in the `cross_realm_bots`
* property of a `/register` response, represented with this type. Doc:
* https://zulip.com/api/register-queue
*
* See also:
* * `User` and its property `is_bot`. Bot users that appear in a single
* realm are, like human users, represented by a `User` value.
* * `UserOrBot`, a convenience union
*/
export type CrossRealmBot = {|
// Property ordering follows the doc.
// Current to feature level (FL) 121.
+user_id: UserId,
+delivery_email?: string | null,
+email: string,
+full_name: string,
// We expect ISO 8601; that's in the doc's example response.
+date_joined: string,
// is_active never appears in `/register` responses, at least up through
// FL 121. The doc wrongly says it always appears. See
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/.60is_active.60.20in.20.60.2Fregister.60.20response/near/1371606
// TODO(server-5.0): New in FL 73
+is_billing_admin?: boolean,
// We assume this is `true`.
+is_bot: true,
// We assume this can't be `null`.
+bot_type: number,
// We assume this is `null` when present. (Cross-realm bots don't have
// owners.)
// TODO(server-3.0): New in FL 1, replacing bot_owner
+bot_owner_id?: null,
// We assume bot_owner is never present, even on old servers. (Cross-realm
// bots don't have owners.)
// TODO(server-3.0): Replaced in FL 1 by bot_owner_id
// +bot_owner?: string,
+role: Role,
// The ? is for future-proofing. For bots it's always '':
// https://github.com/zulip/zulip-mobile/pull/3789#issuecomment-581218576
// so a future version may omit it to reduce payload sizes. See comment
// on the same field in User.
+timezone?: string,
/**
* See note for this property on User.
*/
+avatar_url: AvatarURL,
// If we use this, avoid `avatar_url` falling out of sync with it.
-avatar_version: number,
// (We could be more specific here; but in the interest of reducing
// differences between CrossRealmBot and User, just follow the latter.)
+profile_data?: User['profile_data'],
// We assume this is `true` when present.
// TODO(server-5.0): New in FL 83, replacing is_cross_realm_bot
+is_system_bot?: true,
// We assume this is `true` when present.
// TODO(server-5.0): Replaced in FL 83 by is_system_bot
+is_cross_realm_bot?: true,
|};
/**
* A Zulip user/account, which might be a cross-realm bot.
*/
export type UserOrBot = User | CrossRealmBot;
/**
* For @-mentioning multiple users at once.
*
* When you @-mention a user group, everyone in the group is notified as
* though they were individually mentioned. See user-facing docs:
* https://zulip.com/help/user-groups
*
* This feature is not related to group PMs.
*/
export type UserGroup = $ReadOnly<{|
description: string,
id: number,
members: $ReadOnlyArray<UserId>,
name: string,
|}>;
/**
* A user's chosen availability and text/emoji statuses.
*/
export type UserStatus = $ReadOnly<{|
// true/false: User unavailable/available.
//
// Deprecated: "invisible mode", new in FL 148, doesn't involve this:
// https://chat.zulip.org/#narrow/stream/2-general/topic/.22unavailable.22.20status/near/1454779
// Don't use it except for servers <148.
// TODO(server-6.0): Remove.
away: boolean,
// "foo": User's status text is "foo".
// null: User's status text is unset.
status_text: string | null,
// null: User's status emoji is unset.
status_emoji: null | {|
// These three properties point to an emoji in the same way the same-named
// properties point to an emoji in the Reaction type; see there.
+emoji_name: string,
+reaction_type: ReactionType, // eslint-disable-line no-use-before-define
+emoji_code: string,
|},
|}>;
/**
* The server's representation of the diff between two user-status records.
*
* A value is mentioned only when it's changed.
*
* N.B., emoji statuses are new in 5.0 (feature level 86), so they'll never
* be present before then. But "unchanged" is still appropriate semantics
* and will lead to correct behavior.
*
* For the strings, the empty string means that component of the user's
* status is unset.
*/
// TODO(server-5.0): Simplify jsdoc.
// TODO(docs): All this is observed empirically; update the doc. See
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/Emoji.20statuses.20in.20zulip.2Eyaml/near/1322329
export type UserStatusUpdate = $ReadOnly<{|
away?: boolean,
status_text?: string,
// These three properties point to an emoji in the same way the same-named
// properties point to an emoji in the Reaction type; see there.
emoji_name?: string,
emoji_code?: string,
reaction_type?: ReactionType | '', // eslint-disable-line no-use-before-define
|}>;
/** See ClientPresence, and the doc linked there. */
export type PresenceStatus = 'active' | 'idle' | 'offline';
/**
* A user's presence status, as reported by a specific client.
*
* For an explanation of the Zulip presence model and how to interpret
* `status` and `timestamp`, see the subsystem doc:
* https://zulip.readthedocs.io/en/latest/subsystems/presence.html
*
* @prop timestamp - When the server last heard from this client.
* @prop status - See the presence subsystem doc.
* @prop client
* @prop pushable - Legacy; unused.
*/
export type ClientPresence = $ReadOnly<{|
status: PresenceStatus,
timestamp: number,
client: string,
/**
* Indicates if the client can receive push notifications. This property
* was intended for showing a user's presence status as "on mobile" if
* they are inactive on all devices but can receive push notifications
* (see zulip/zulip bd20a756f9). However, this property doesn't seem to be
* used anywhere on the web app or the mobile client, and can be
* considered legacy.
*
* Empirically this is `boolean` on at least some clients, and absent on
* `aggregated`. By writing `empty` we make it an error to actually use it.
*/
pushable?: empty,
|}>;
/**
* A user's presence status, including all information from all their clients.
*
* The `aggregated` property equals one of the others. For details, see:
* https://zulip.com/api/get-user-presence
*
* See also the app's `getAggregatedPresence`, which reimplements a version
* of the logic to compute `aggregated`.
*/
export type UserPresence = $ReadOnly<{|
aggregated: ClientPresence,
[client: string]: ClientPresence,
|}>;
/**
* A snapshot of the presence status of all users.
*
* See `UserPresence` and `ClientPresence` for more, and links.
*/
export type PresenceSnapshot = {| +[email: string]: UserPresence |};
/** This is what appears in the `muted_users` server event.
* See https://chat.zulip.org/api/get-events#muted_users for details.
*/
export type MutedUser = $ReadOnly<{|
id: UserId,
timestamp: number,
|}>;
//
//
//
// ===================================================================
// Streams, topics, and stuff about them.
//
//
export type Stream = {|
// Based on `streams` in the /register response, current to FL 121:
// https://zulip.com/api/register-queue
// Property ordering follows that doc.
+stream_id: number,
+name: string,
+description: string,
// TODO(server-4.0): New in FL 30.
+date_created?: number,
// Note: updateStream controls this with its `is_private` param.
+invite_only: boolean,
+rendered_description: string,
// TODO(server-2.1): is_web_public was added in Zulip version 2.1;
// absence implies the stream is not web-public.
+is_web_public?: boolean,
// TODO(server-3.0): Added in FL 1; if absent, use is_announcement_only.
+stream_post_policy?: 1 | 2 | 3 | 4,
// Special values are:
// * null: default; inherits from org-level setting
// * -1: unlimited retention
// These special values differ from updateStream's and createStream's params; see
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/message_retention_days/near/1367895
// TODO(server-3.0): New in FL 17.
+message_retention_days?: number | null,
+history_public_to_subscribers: boolean,
+first_message_id: number | null,
// TODO(server-3.0): Deprecated at FL 1; use stream_post_policy instead
+is_announcement_only: boolean,
|};
export type Subscription = {|
...Stream,
+color: string,
+in_home_view: boolean,
+pin_to_top: boolean,
+email_address: string,
+is_old_stream: boolean,
+stream_weekly_traffic: number,
/** (To interpret this value, see `getIsNotificationEnabled`.) */
+push_notifications: null | boolean,
// To meaningfully interpret these, we'll need logic similar to that for
// `push_notifications`. Pending that, the `-` ensures we don't read them.
-audible_notifications: boolean,
-desktop_notifications: boolean,
|};
export type Topic = $ReadOnly<{|
name: string,
max_id: number,
|}>;
/**
* A user's policy for whether and how to see a particular topic.
*
* See `visibility_policy` at: https://zulip.com/api/get-events#user_topic
*/
export enum UserTopicVisibilityPolicy {
None = 0,
Muted = 1,
Unmuted = 2,
// Not in the API docs yet. But it is, uh, in the server implementation:
// https://github.com/zulip/zulip/blob/2ec8273c6/zerver/models.py#L2794-L2797
// TODO(server): delete this comment once documented
Followed = 3,
}
/**
* A user's relationship to a topic; in particular, muting.
*
* Found in the initial data at `user_topics`, and in `user_topic` events:
* https://zulip.com/api/get-events#user_topic
*/
export type UserTopic = {|
+stream_id: number,
+topic_name: string,
+last_updated: number,
+visibility_policy: UserTopicVisibilityPolicy,
|};
/**
* A muted topic.
*
* Found in the initial data, and in `muted_topics` events.
*
* The elements are the stream name, then topic, then possibly timestamp.
*/
// Server issue for using stream IDs (#3918) for muted topics, not names:
// https://github.com/zulip/zulip/issues/21015
// TODO(server-3.0): Simplify away the no-timestamp version, new in FL 1.
// TODO(server-6.0): Remove, in favor of UserTopic.
export type MutedTopicTuple = [string, string] | [string, string, number];
//
//
//
// ===================================================================
// Narrows.
//
//
// See docs: https://zulip.com/api/construct-narrow
// prettier-ignore
/* eslint-disable semi-style */
export type NarrowElement =
| {| +operator: 'is' | 'in' | 'topic' | 'search', +operand: string |}
// The server started accepting numeric user IDs and stream IDs in 2.1:
// * `stream` since 2.1-dev-2302-g3680393b4
// * `group-pm-with` since 2.1-dev-1813-gb338fd130
// * `sender` since 2.1-dev-1812-gc067c155a
// * `pm-with` since 2.1-dev-1350-gd7b4de234
// TODO(server-2.1): Stop sending stream names (#3918) or user emails (#3764) here.
| {| +operator: 'stream', +operand: string | number |} // stream ID
| {| +operator: 'pm-with', +operand: string | $ReadOnlyArray<UserId> |}
| {| +operator: 'sender', +operand: string | UserId |}
| {| +operator: 'near' | 'id', +operand: number |} // message ID
// ('group-pm-with' was accepted at least through server 6.x, but support
// will be dropped: https://github.com/zulip/zulip/issues/24806 )
;
/**
* A narrow, in the form used in the Zulip API at get-messages.
*
* See also `Narrow` in the non-API app code, which describes how we
* represent narrows within the app.
*/
export type ApiNarrow = $ReadOnlyArray<NarrowElement>;
//
//
//
// ===================================================================
// Messages and things attached to them.
//
//
/**
* Type of an emoji reaction to a message.
*
* These correspond to the values allowed for Reaction.reaction_type in the
* server's models. The values are:
* * unicode_emoji: An emoji found in Unicode, corresponding to a sequence
* of Unicode code points. The list of these depends on the Zulip
* server's version.
* * realm_emoji: A custom emoji uploaded by some user on a given realm.
* * zulip_extra_emoji: An emoji distributed with Zulip, like :zulip:.
*
* See `Reaction` which uses this.
*/
export type ReactionType = 'unicode_emoji' | 'realm_emoji' | 'zulip_extra_emoji';
/**
* An emoji reaction to a message.
*
* The raw JSON from the server may have a different structure, but we
* normalize it to this form.
*/
export type Reaction = $ReadOnly<{|
user_id: UserId,
emoji_name: string,
reaction_type: ReactionType,
/**
* A string that uniquely identifies a particular emoji.
*
* The format varies with `reaction_type`, and can be subtle.
* See the comment on Reaction.emoji_code here:
* https://github.com/zulip/zulip/blob/main/zerver/models.py
*/
emoji_code: string,
|}>;
/**
* "Snapshot" objects from https://zulip.com/api/get-message-history .
*
* See also `MessageEdit`.
*/
// TODO: Probably not current? Sync with the doc.
export type MessageSnapshot = $ReadOnly<{|
user_id: UserId,
timestamp: number,
/** Docs unclear but suggest absent if only content edited. */
topic?: string,
/**
* Docs unclear, but suggest these five absent if only topic edited.
* They definitely say "prev"/"diff" properties absent on the first snapshot.
*/
content?: string,
rendered_content?: string,
prev_content?: string,
prev_rendered_content?: string,
content_html_diff?: string,
|}>;
/**
* Found in the history within a `Message` object, from servers with FL 118+.
*
* Servers older than 118 send edit-history data in a different shape that's
* awkward to work with. In that case we drop the data on the floor, not
* storing it in Redux, so we can focus on handling the new, better shape.
*
* See also `MessageSnapshot`.
*/
export type MessageEdit = $ReadOnly<{|
prev_content?: string,
prev_rendered_content?: string,
prev_rendered_content_version?: number,
prev_stream?: number,
prev_topic?: string,
stream?: number,
timestamp: number,
topic?: string,
user_id: UserId | null,
|}>;
/** A user, as seen in the `display_recipient` of a PM `Message`. */
export type PmRecipientUser = $ReadOnly<{|
// These five fields (id, email, full_name, short_name, is_mirror_dummy)
// have all been present since server commit 6b13f4a3c, in 2014.
id: UserId,
email: string,
full_name: string,
// We mark short_name and is_mirror_dummy optional so we can leave them
// out of Outbox values; we never rely on them anyway.
short_name?: string,
is_mirror_dummy?: boolean,
|}>;
/**
* The data encoded in a submessage to make the message a widget.
*
* Note that future server versions might introduce new types of widgets, so
* `widget_type` could be a value not included here. But when it is one of
* these values, the rest of the object will follow this type.
*/
// Ideally we'd be able to express both the known and the unknown widget
// types: we'd have another branch of this union which looked like
// | {| +widget_type: (string *other than* those above), +extra_data?: { ... } |}
// But there doesn't seem to be a way to express that in Flow.
export type WidgetData =
| {|
+widget_type: 'poll',
+extra_data?: {| +question?: string, +options?: $ReadOnlyArray<string> |},
|}
// We can write these down more specifically when we implement these widgets.
| {| +widget_type: 'todo', +extra_data?: { ... } |}
| {| +widget_type: 'zform', +extra_data?: { ... } |};
/**
* The data encoded in a submessage that acts on a widget.
*
* The interpretation of this data, including the meaning of the `type`
* field, is specific to each widget type.
*
* We delegate actually processing these to shared code, so we don't specify
* the details further.
*/
export type WidgetEventData = { +type: string, ... };
/**
* The data encoded in a `Submessage`.
*
* For widgets (the only existing use of submessages), the submessages array
* consists of:
* * One submessage with `WidgetData`; then
* * Zero or more submessages with `WidgetEventData`.
*/
export type SubmessageData = WidgetData | WidgetEventData;
/**
* Submessages are items containing extra data that can be added to a
* message. Despite what their name might suggest, they are not a subtype
* of the `Message` type, nor do they share almost any fields with it.
*
* Submessages are used by Widgets:
* https://zulip.readthedocs.io/en/latest/subsystems/widgets.html
*
* Normally a message contains an empty array of these. We differentiate
* between a normal message and a widget by the length of `submessages`
* array property. Widgets will have 1 or more submessages.
*/
export type Submessage = $ReadOnly<{|
id: number,
message_id: number,
sender_id: UserId,
msg_type: 'widget', // only this type is currently available
/** A `SubmessageData` object, JSON-encoded. */
content: string,
|}>;
/**
* A flag that can be set on a message for a user.
*
* See:
* https://zulip.com/api/update-message-flags#available-flags
*/
export type UserMessageFlag =
| 'read'
| 'starred'
| 'collapsed'
| 'mentioned'
| 'wildcard_mentioned'
| 'has_alert_word'
| 'historical';
/**
* Properties in common among the two different flavors of a
* `Message`: `PmMessage` and `StreamMessage`.
*/
type MessageBase = $ReadOnly<{|
//
// Properties on `event.message` in `message` events. Order follows the
// doc: https://zulip.com/api/get-events#message
// See comment on Message for how up-to-date these are.
//
/**
* Present under EVENT_NEW_MESSAGE and under MESSAGE_FETCH_COMPLETE,
* and in `state.messages`, all as an AvatarURL, because we
* translate into that form at the edge.
*
* For how it appears at the edge (and how we translate) see
* AvatarURL.fromUserOrBotData.
*/
avatar_url: AvatarURL,
client: string,
content: string,
content_type:
| 'text/html' // message requested with apply_markdown true
| 'text/x-markdown', // message requested with apply_markdown false
// display_recipient handled on PmMessage and StreamMessage separately
/**
* A view of the message's edit history, if available.
*
* This is null if it's coming from a server with FL <118; see comment on
* MessageEdit.
*
* Null if the realm doesn't allow viewing edit history.
*
* Missing/undefined if the message had no edit history when we added it
* to the state.
*/
// TODO: Keep current:
// - Handle changes to the allow_edit_history setting:
// - Treat an allow_edit_history change similar to a restart event (in
// particular, a restart event where the server version changed, which
// could mean any number of subtle changes in server behavior), and do
// a re-fetch of server data soon after it.
//
// TODO(server-5.0): Remove FL <118 condition
//
// (Why optional and `| void`? We convert missing to undefined at the
// edge, but that's incidental and could easily change.)
edit_history?: $ReadOnlyArray<MessageEdit> | null | void,
id: number,
is_me_message: boolean,
last_edit_timestamp?: number,
reactions: $ReadOnlyArray<Reaction>,
/** Deprecated; a server implementation detail not useful in a client. */
// recipient_id: number,
sender_email: string,
sender_full_name: string,
sender_id: UserId,
sender_realm_str: string,
// Don't use. Likely removed everywhere in FL 26, but the changelog only
// mentions GET /messages: https://zulip.com/api/changelog#changes-in-zulip-31
// TODO(server-3.1): Remove.
sender_short_name?: string,
// stream_id handled on StreamMessage
// subject handled on StreamMessage
/** Servers <1.9.0 omit this; when omitted, equivalent to empty array. */
// The doc is wrong to say this is (string)[]; see
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/.60.2Esubmessages.60.20on.20message.20objects/near/1389473
// TODO(server-1.9): Make required.
submessages?: $ReadOnlyArray<Submessage>,
timestamp: number,
// topic_links handled on StreamMessage
// type handled on PmMessage and StreamMessage separately
//
// Special-case properties; see comments
//
/**
* The `flags` story is a bit complicated:
* * Absent in `event.message` for a `message` event... but instead
* (confusingly) there is an `event.flags`.
* * Present under `EVENT_NEW_MESSAGE`.
* * Present under `MESSAGE_FETCH_COMPLETE`, and in the server `/message`
* response that action is based on.
* * Absent in the Redux `state.messages`; we move the information to a
* separate subtree `state.flags`.
*/
flags?: $ReadOnlyArray<UserMessageFlag>,
/** Our own flag; if true, really type `Outbox`. */
isOutbox?: false,
/**
* These don't appear in `message` events, but they appear in a `/message`
* response when a search is involved.
*/
match_content?: string,
match_subject?: string,
|}>;
export type PmMessage = $ReadOnly<{|
...MessageBase,
type: 'private',
// Notes from studying the server code:
// * Notes are primarily from the server as of 2020-04 at cb85763c7, but
// this logic is very stable; confirmed all points about behavior as of
// 1.8.0 (from 2018-04), too.
//
// * This field is ultimately computed (for both events and /messages