Skip to content

Commit 2106381

Browse files
chrisbobbegnprice
authored andcommitted
user status: Sync server-data types with server behavior; rework model
As mentioned in some comments, the server-data types and comments are based on my empirical observations reported in https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/Emoji.20statuses.20in.20zulip.2Eyaml/near/1322329 ; those should eventually make it into the server API doc.
1 parent 4bdf6bd commit 2106381

File tree

11 files changed

+256
-116
lines changed

11 files changed

+256
-116
lines changed

src/account-info/AccountDetails.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default function AccountDetails(props: Props): Node {
6767
/>
6868
<ZulipText style={[styles.largerText, styles.halfMarginRight]} text={user.full_name} />
6969
</View>
70-
{userStatusText !== undefined && (
70+
{userStatusText !== null && (
7171
<ZulipText style={[styles.largerText, componentStyles.statusText]} text={userStatusText} />
7272
)}
7373
{!isSelf && (

src/api/eventTypes.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@
1010
* @flow strict-local
1111
*/
1212

13-
import type { Message, MutedUser, Stream, UserId, UserPresence } from './modelTypes';
13+
import type {
14+
Message,
15+
MutedUser,
16+
Stream,
17+
UserId,
18+
UserPresence,
19+
UserStatusUpdate,
20+
} from './modelTypes';
1421
import type { RealmDataForUpdate } from './realmDataTypes';
1522

1623
export class EventTypes {
@@ -101,25 +108,19 @@ export type PresenceEvent = $ReadOnly<{|
101108
|}>;
102109

103110
/**
104-
* Updates the user status for a user
111+
* New value for a user's chosen availability and/or text/emoji status
105112
*
106-
* @prop [away] - update user's away status:
107-
* - `true` the user is away regardless of presence
108-
* - `false` remove the away status, now use presence
109-
* @prop [status_text] - if present:
110-
* - empty string clears the user's status text
111-
* - any string sets user's status to that
112-
*
113-
* Not providing a property means 'leave this value unchanged'
113+
* https://zulip.com/api/get-events#user_status
114114
*
115115
* See InitialDataUserStatus for the corresponding initial data.
116116
*/
117117
export type UserStatusEvent = $ReadOnly<{|
118118
...EventCommon,
119119
type: typeof EventTypes.user_status,
120120
user_id: UserId,
121-
away?: boolean,
122-
status_text?: string,
121+
122+
// How the status is updated from the old value.
123+
...UserStatusUpdate,
123124
|}>;
124125

125126
type StreamListEvent = $ReadOnly<{|

src/api/initialDataTypes.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* @flow strict-local */
22

3+
import type { UserStatusUpdate } from './modelTypes';
34
import type {
45
CrossRealmBot,
56
MutedTopicTuple,
@@ -497,6 +498,13 @@ export type InitialDataUpdateMessageFlags = $ReadOnly<{|
497498
|}>,
498499
|}>;
499500

501+
/**
502+
* Users' chosen availability and text/emoji statuses.
503+
*
504+
* See UserStatusEvent for the event that carries updates to this data.
505+
*/
506+
// TODO(?): Find out if servers ignore any users (e.g., deactivated ones) when
507+
// assembling this.
500508
export type InitialDataUserStatus = $ReadOnly<{|
501509
/**
502510
* Older servers (through at least 1.9.1) don't send this.
@@ -511,16 +519,24 @@ export type InitialDataUserStatus = $ReadOnly<{|
511519
*/
512520
// TODO(server-1.9.1): Make required.
513521
user_status?: $ReadOnly<{|
522+
// How the status has changed relative to the following "zero" value:
523+
//
524+
// {
525+
// away: false,
526+
// status_text: '',
527+
// emoji_name: '',
528+
// reaction_type: '',
529+
// emoji_code: '',
530+
// };
531+
//
532+
// (For how we represent that zero value as a UserStatus in our model,
533+
// see `kUserStatusZero`.)
534+
//
535+
// If no change, servers may omit the record for that user entirely.
536+
//
514537
// Keys are UserId encoded as strings (just because JS objects are
515538
// string-keyed).
516-
[userId: string]: $ReadOnly<{|
517-
// TODO: add status emoji properties
518-
// TODO: Comment on what these mean (if doc not fixed):
519-
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/Emoji.20statuses.20in.20zulip.2Eyaml/near/1322329
520-
521-
away?: true,
522-
status_text?: string,
523-
|}>,
539+
[userId: string]: UserStatusUpdate,
524540
|}>,
525541
|}>;
526542

src/api/modelTypes.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,32 @@ export type UserGroup = $ReadOnly<{|
211211
name: string,
212212
|}>;
213213

214+
/**
215+
* The server's representation of the diff between two user-status records.
216+
*
217+
* A value is mentioned only when it's changed.
218+
*
219+
* N.B., emoji statuses are new in 5.0 (feature level 86), so they'll never
220+
* be present before then. But "unchanged" is still appropriate semantics
221+
* and will lead to correct behavior.
222+
*
223+
* For the strings, the empty string means that component of the user's
224+
* status is unset.
225+
*/
226+
// TODO(server-5.0): Simplify jsdoc.
227+
// TODO(docs): All this is observed empirically; update the doc. See
228+
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/Emoji.20statuses.20in.20zulip.2Eyaml/near/1322329
229+
export type UserStatusUpdate = $ReadOnly<{|
230+
away?: boolean,
231+
status_text?: string,
232+
233+
// These three properties point to an emoji in the same way the same-named
234+
// properties point to an emoji in the Reaction type; see there.
235+
emoji_name?: string,
236+
emoji_code?: string,
237+
reaction_type?: ReactionType | '', // eslint-disable-line no-use-before-define
238+
|}>;
239+
214240
/** See ClientPresence, and the doc linked there. */
215241
export type PresenceStatus = 'active' | 'idle' | 'offline';
216242

src/api/users/updateUserStatus.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ type UserStatusParams = {|
99

1010
// TODO: Take a partial UserStatus object from our user-status model.
1111
// Partial because servers allow updating just part of the status in a
12-
// request, and it's nice to let callers do that. First need to make
13-
// missing/`undefined` stop being meaningful representations in any of
14-
// UserStatus's keys.
12+
// request, and it's nice to let callers do that.
1513
export default (auth: Auth, params: UserStatusParams): Promise<ApiResponseSuccess> =>
1614
apiPost(auth, 'users/me/status', params);

src/reduxTypes.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import type Immutable from 'immutable';
1111
import type { InputSelector } from 'reselect';
1212

13-
import type { Account, Outbox } from './types';
13+
import type { Account, Outbox, ReactionType } from './types';
1414
import type { Action, DispatchableWithoutAccountAction } from './actionTypes';
1515
import type {
1616
Topic,
@@ -383,18 +383,24 @@ export type TypingState = $ReadOnly<{|
383383
export type UserGroupsState = $ReadOnlyArray<UserGroup>;
384384

385385
/**
386-
* Specifies user status related properties
387-
* @prop away - present if we are to override user's presence status
388-
* * This is the "user status" / "unavailable" feature added in early 2019.
389-
* (At time of writing, there are no docs to link to.)
390-
* @prop status_text - a string representing information the user decided to
391-
* manually set as their 'current status'
386+
* A user's chosen availability and text/emoji statuses.
392387
*/
393-
// TODO: fix types, implementation, and jsdoc:
394-
// https://chat.zulip.org/#narrow/stream/412-api-documentation/topic/Emoji.20statuses.20in.20zulip.2Eyaml/near/1322329
395388
export type UserStatus = $ReadOnly<{|
396-
away?: true,
397-
status_text?: string,
389+
// true/false: User unavailable/available.
390+
away: boolean,
391+
392+
// "foo": User's status text is "foo".
393+
// null: User's status text is unset.
394+
status_text: string | null,
395+
396+
// null: User's status emoji is unset.
397+
status_emoji: null | {|
398+
// These three properties point to an emoji in the same way the same-named
399+
// properties point to an emoji in the Reaction type; see there.
400+
+emoji_name: string,
401+
+reaction_type: ReactionType,
402+
+emoji_code: string,
403+
|},
398404
|}>;
399405

400406
export type UserStatusState = Immutable.Map<UserId, UserStatus>;

src/user-status/UserStatusScreen.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ export default function UserStatusScreen(props: Props): Node {
3838
const auth = useSelector(getAuth);
3939
const userStatusText = useSelector(getSelfUserStatusText);
4040

41-
const [statusText, setStatusText] = useState<string | void>(userStatusText);
41+
const [statusText, setStatusText] = useState<string | null>(userStatusText);
4242
const _ = useContext(TranslationContext);
4343

4444
const sendToServer = useCallback(
45-
(_statusText: string | void) => {
45+
(_statusText: string | null) => {
4646
api.updateUserStatus(auth, { status_text: _statusText ?? '' });
4747
NavigationService.dispatch(navigateBack());
4848
},
@@ -54,7 +54,7 @@ export default function UserStatusScreen(props: Props): Node {
5454
}, [statusText, sendToServer]);
5555

5656
const handlePressClear = useCallback(() => {
57-
setStatusText(undefined);
57+
setStatusText(null);
5858
sendToServer('');
5959
}, [sendToServer]);
6060

0 commit comments

Comments
 (0)