Skip to content

Commit ea170ba

Browse files
committed
presence [nfc]: Modelify statusFromPresence, as getPresenceOnlyStatusForUser
This new function takes the whole PresenceState as an argument, instead of a small fragment of it, which will help us as we add new wrinkles to that state for #5669. It also returns null for missing data, rather than picking a value; it's then up to the caller to choose how to handle that.
1 parent 0306c47 commit ea170ba

File tree

3 files changed

+50
-46
lines changed

3 files changed

+50
-46
lines changed

src/presence/__tests__/presenceModel-test.js

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { PRESENCE_RESPONSE, EVENT_PRESENCE } from '../../actionConstants';
88
import {
99
reducer as presenceReducer,
1010
getAggregatedPresence,
11-
statusFromPresence,
1211
getUserLastActiveAsRelativeTimeString,
1312
getPresenceStatusForUserId,
13+
getPresenceOnlyStatusForUser,
1414
} from '../presenceModel';
1515
import { makePresenceState } from './presence-testlib';
1616
import type { UserPresence, UserStatus } from '../../api/modelTypes';
@@ -144,48 +144,52 @@ describe('getUserLastActiveAsRelativeTimeString', () => {
144144
});
145145
});
146146

147-
describe('statusFromPresence', () => {
147+
describe('getPresenceOnlyStatusForUser', () => {
148148
test('if aggregate status is "offline" the result is always "offline"', () => {
149+
const userPresence = {
150+
aggregated: { client: 'website', status: 'offline', timestamp: currentTimestamp - 100 },
151+
};
149152
expect(
150-
statusFromPresence({
151-
aggregated: { client: 'website', status: 'offline', timestamp: currentTimestamp - 100 },
152-
}),
153+
getPresenceOnlyStatusForUser(makePresenceState([[eg.otherUser, userPresence]]), eg.otherUser),
153154
).toBe('offline');
154155
});
155156

156157
test('if status is not "offline" but last activity was more than 1hr ago result is still "offline"', () => {
158+
const userPresence = {
159+
aggregated: {
160+
client: 'website',
161+
status: 'active',
162+
timestamp: Math.trunc(Date.now() / 1000 - 2 * 60 * 60), // two hours
163+
},
164+
};
157165
expect(
158-
statusFromPresence({
159-
aggregated: {
160-
client: 'website',
161-
status: 'active',
162-
timestamp: Math.trunc(Date.now() / 1000 - 2 * 60 * 60), // two hours
163-
},
164-
}),
166+
getPresenceOnlyStatusForUser(makePresenceState([[eg.otherUser, userPresence]]), eg.otherUser),
165167
).toBe('offline');
166168
});
167169

168170
test('if status is "idle" and last activity is less than 140 seconds then result remain "idle"', () => {
171+
const userPresence = {
172+
aggregated: {
173+
client: 'website',
174+
status: 'idle',
175+
timestamp: Math.trunc(Date.now() / 1000 - 60), // 1 minute
176+
},
177+
};
169178
expect(
170-
statusFromPresence({
171-
aggregated: {
172-
client: 'website',
173-
status: 'idle',
174-
timestamp: Math.trunc(Date.now() / 1000 - 60), // 1 minute
175-
},
176-
}),
179+
getPresenceOnlyStatusForUser(makePresenceState([[eg.otherUser, userPresence]]), eg.otherUser),
177180
).toBe('idle');
178181
});
179182

180183
test('if status is not "offline" and last activity was less than 5min ago result is "active"', () => {
184+
const userPresence = {
185+
aggregated: {
186+
client: 'website',
187+
status: 'active',
188+
timestamp: Math.trunc(Date.now() / 1000 - 60), // 1 minute
189+
},
190+
};
181191
expect(
182-
statusFromPresence({
183-
aggregated: {
184-
client: 'website',
185-
status: 'active',
186-
timestamp: Math.trunc(Date.now() / 1000 - 60), // 1 minute
187-
},
188-
}),
192+
getPresenceOnlyStatusForUser(makePresenceState([[eg.otherUser, userPresence]]), eg.otherUser),
189193
).toBe('active');
190194
});
191195
});

src/presence/presenceModel.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import type { UserOrBot } from '../api/modelTypes';
3333

3434
export const getPresence = (state: PerAccountState): PresenceState => state.presence;
3535

36-
export function getUserPresenceByEmail(state: PresenceState, email: string): UserPresence | void {
36+
function getUserPresenceByEmail(state: PresenceState, email: string): UserPresence | void {
3737
return state.byEmail.get(email);
3838
}
3939

@@ -143,24 +143,29 @@ export function getUserLastActiveAsRelativeTimeString(
143143
: `${formatDistanceToNow(lastTimeActive)} ago`;
144144
}
145145

146-
export const statusFromPresence = (presence: UserPresence | void): PresenceStatus => {
147-
if (!presence || !presence.aggregated) {
148-
return 'offline';
146+
export function getPresenceOnlyStatusForUser(
147+
state: PresenceState,
148+
user: UserOrBot,
149+
): PresenceStatus | null {
150+
const userPresence = getUserPresenceByEmail(state, user.email);
151+
if (!userPresence || !userPresence.aggregated) {
152+
return null;
149153
}
154+
const { status, timestamp } = userPresence.aggregated;
150155

151-
if (presence.aggregated.status === 'offline') {
156+
if (status === 'offline') {
152157
return 'offline';
153158
}
154159

155-
const timestampDate = new Date(presence.aggregated.timestamp * 1000);
160+
const timestampDate = new Date(timestamp * 1000);
156161
const diffToNowInSeconds = differenceInSeconds(Date.now(), timestampDate);
157162

158163
if (diffToNowInSeconds > OFFLINE_THRESHOLD_SECS) {
159164
return 'offline';
160165
}
161166

162-
return presence.aggregated.status;
163-
};
167+
return status;
168+
}
164169

165170
/**
166171
* Get a user's overall presence status, aggregated from all their devices.
@@ -183,12 +188,8 @@ export const getPresenceStatusForUserId = (
183188
if (!user) {
184189
return null;
185190
}
186-
const userPresence = getUserPresenceByEmail(presence, user.email);
187-
if (!userPresence || !userPresence.aggregated) {
188-
return null;
189-
}
190191

191-
return statusFromPresence(userPresence);
192+
return getPresenceOnlyStatusForUser(presence, user);
192193
};
193194

194195
//

src/users/userHelpers.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import * as typeahead from '@zulip/shared/lib/typeahead';
55

66
import type {
77
MutedUsersState,
8-
UserPresence,
98
UserId,
109
UserGroup,
1110
PresenceState,
11+
PresenceStatus,
1212
UserOrBot,
1313
} from '../types';
1414
import { ensureUnreachable } from '../types';
15-
import { getUserPresenceByEmail, statusFromPresence } from '../presence/presenceModel';
15+
import { getPresenceOnlyStatusForUser } from '../presence/presenceModel';
1616

1717
type UsersByStatus = {|
1818
active: UserOrBot[],
@@ -27,14 +27,13 @@ export const groupUsersByStatus = (
2727
): UsersByStatus => {
2828
const groupedUsers = { active: [], idle: [], offline: [], unavailable: [] };
2929
users.forEach(user => {
30-
const status = statusFromPresence(getUserPresenceByEmail(presences, user.email));
30+
const status = getPresenceOnlyStatusForUser(presences, user) ?? 'offline';
3131
groupedUsers[status].push(user);
3232
});
3333
return groupedUsers;
3434
};
3535

36-
const statusOrder = (presence: UserPresence | void): number => {
37-
const status = statusFromPresence(presence);
36+
const statusOrder = (status: PresenceStatus): number => {
3837
switch (status) {
3938
case 'active':
4039
return 1;
@@ -54,8 +53,8 @@ export const sortUserList = (
5453
): $ReadOnlyArray<UserOrBot> =>
5554
[...users].sort(
5655
(x1, x2) =>
57-
statusOrder(getUserPresenceByEmail(presences, x1.email))
58-
- statusOrder(getUserPresenceByEmail(presences, x2.email))
56+
statusOrder(getPresenceOnlyStatusForUser(presences, x1) ?? 'offline')
57+
- statusOrder(getPresenceOnlyStatusForUser(presences, x2) ?? 'offline')
5958
|| x1.full_name.toLowerCase().localeCompare(x2.full_name.toLowerCase()),
6059
);
6160

0 commit comments

Comments
 (0)