Skip to content

Commit 6b4c569

Browse files
committed
model: Add hasPassedWaitingPeriod helper method to PerAccountStore
Also add `UserRole.isAtLeast` method.
1 parent a06b1d5 commit 6b4c569

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

lib/api/model/model.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,11 @@ enum UserRole{
303303
final int? apiValue;
304304

305305
int? toJson() => apiValue;
306+
307+
bool isAtLeast(UserRole threshold) {
308+
// Roles with more privilege have lower [apiValue].
309+
return apiValue! <= threshold.apiValue!;
310+
}
306311
}
307312

308313
/// As in `streams` in the initial snapshot.

lib/model/store.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,28 @@ class PerAccountStore extends ChangeNotifier with EmojiStore, ChannelStore, Mess
434434

435435
final TypingStatus typingStatus;
436436

437+
/// Whether [user] has passed the realm's waiting period to be a full member.
438+
///
439+
/// See:
440+
/// https://zulip.com/api/roles-and-permissions#determining-if-a-user-is-a-full-member
441+
///
442+
/// To determine if a user is a full member, callers must also check that the
443+
/// user's role is at least [UserRole.member].
444+
bool hasPassedWaitingPeriod(User user, {required DateTime byDate}) {
445+
// [User.dateJoined] is in UTC. For logged-in users, the format is:
446+
// YYYY-MM-DDTHH:mm+00:00, which includes the timezone offset for UTC.
447+
// For logged-out spectators, the format is: YYYY-MM-DD, which doesn't
448+
// include the timezone offset. In the later case, [DateTime.parse] will
449+
// interpret it as the client's local timezone, which could lead to
450+
// incorrect results; but that's acceptable for now because the app
451+
// doesn't support viewing as a spectator.
452+
//
453+
// See the related discussion:
454+
// https://chat.zulip.org/#narrow/channel/412-api-documentation/topic/provide.20an.20explicit.20format.20for.20.60realm_user.2Edate_joined.60/near/1980194
455+
final dateJoined = DateTime.parse(user.dateJoined);
456+
return byDate.difference(dateJoined).inDays >= realmWaitingPeriodThreshold;
457+
}
458+
437459
////////////////////////////////
438460
// Streams, topics, and stuff about them.
439461

test/model/store_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,28 @@ void main() {
247247
});
248248
});
249249

250+
group('PerAccountStore.hasPassedWaitingPeriod', () {
251+
final store = eg.store(initialSnapshot:
252+
eg.initialSnapshot(realmWaitingPeriodThreshold: 2));
253+
254+
final testCases = [
255+
('2024-11-25T10:00+00:00', DateTime.utc(2024, 11, 25 + 0, 10, 00), false),
256+
('2024-11-25T10:00+00:00', DateTime.utc(2024, 11, 25 + 1, 10, 00), false),
257+
('2024-11-25T10:00+00:00', DateTime.utc(2024, 11, 25 + 2, 09, 59), false),
258+
('2024-11-25T10:00+00:00', DateTime.utc(2024, 11, 25 + 2, 10, 00), true),
259+
('2024-11-25T10:00+00:00', DateTime.utc(2024, 11, 25 + 1000, 07, 00), true),
260+
];
261+
262+
for (final (String dateJoined, DateTime currentDate, bool hasPassedWaitingPeriod) in testCases) {
263+
test('user joined at $dateJoined ${hasPassedWaitingPeriod ? 'has' : "hasn't"} '
264+
'passed waiting period by $currentDate', () {
265+
final user = eg.user(dateJoined: dateJoined);
266+
check(store.hasPassedWaitingPeriod(user, byDate: currentDate))
267+
.equals(hasPassedWaitingPeriod);
268+
});
269+
}
270+
});
271+
250272
group('PerAccountStore.handleEvent', () {
251273
// Mostly this method just dispatches to ChannelStore and MessageStore etc.,
252274
// and so most of the tests live in the test files for those

0 commit comments

Comments
 (0)