Skip to content

Commit 4a77fbd

Browse files
chrisbobbegnprice
authored andcommitted
api: Start accepting null for user_settings.twenty_four_hour_time
Servers can't yet start sending null without breaking clients. Releasing this will start lowering the number of client installs that would break, and eventually there will be few enough that the breakage is acceptable; see discussion (same link as in comment): https://chat.zulip.org/#narrow/channel/378-api-design/topic/.60user_settings.2Etwenty_four_hour_time.60/near/2220696
1 parent 0a6b2f7 commit 4a77fbd

File tree

9 files changed

+80
-17
lines changed

9 files changed

+80
-17
lines changed

lib/api/model/events.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class UserSettingsUpdateEvent extends Event {
175175
final value = json['value'];
176176
switch (UserSettingName.fromRawString(json['property'] as String)) {
177177
case UserSettingName.twentyFourHourTime:
178+
return TwentyFourHourTimeMode.fromApiValue(value as bool?);
178179
case UserSettingName.displayEmojiReactionUsers:
179180
return value as bool;
180181
case UserSettingName.emojiset:

lib/api/model/initial_snapshot.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,12 @@ class RecentDmConversation {
259259
/// in <https://zulip.com/api/register-queue>.
260260
@JsonSerializable(fieldRename: FieldRename.snake, createFieldMap: true)
261261
class UserSettings {
262-
bool twentyFourHourTime;
262+
@JsonKey(
263+
fromJson: TwentyFourHourTimeMode.fromApiValue,
264+
toJson: TwentyFourHourTimeMode.staticToJson,
265+
)
266+
TwentyFourHourTimeMode twentyFourHourTime;
267+
263268
bool? displayEmojiReactionUsers; // TODO(server-6)
264269
Emojiset emojiset;
265270
bool presenceEnabled;

lib/api/model/initial_snapshot.g.dart

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/api/model/model.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,35 @@ enum UserSettingName {
298298
String toJson() => _$UserSettingNameEnumMap[this]!;
299299
}
300300

301+
/// A value from [UserSettings.twentyFourHourTime].
302+
enum TwentyFourHourTimeMode {
303+
twelveHour(apiValue: false),
304+
twentyFourHour(apiValue: true),
305+
306+
/// The locale's default format (12-hour for en_US, 24-hour for fr_FR, etc.).
307+
// TODO(#1727) actually follow this
308+
// Not sent by current servers, but planned when most client installs accept it:
309+
// https://chat.zulip.org/#narrow/channel/378-api-design/topic/.60user_settings.2Etwenty_four_hour_time.60/near/2220696
310+
// TODO(server-future) Write down what server N starts sending null;
311+
// adjust the comment; leave a TODO(server-N) to delete the comment
312+
localeDefault(apiValue: null),
313+
;
314+
315+
const TwentyFourHourTimeMode({required this.apiValue});
316+
317+
final bool? apiValue;
318+
319+
static bool? staticToJson(TwentyFourHourTimeMode instance) => instance.apiValue;
320+
321+
bool? toJson() => TwentyFourHourTimeMode.staticToJson(this);
322+
323+
static TwentyFourHourTimeMode fromApiValue(bool? value) => switch (value) {
324+
false => twelveHour,
325+
true => twentyFourHour,
326+
null => localeDefault,
327+
};
328+
}
329+
301330
/// As in [UserSettings.emojiset].
302331
@JsonEnum(fieldRename: FieldRename.kebab, alwaysCreate: true)
303332
enum Emojiset {

lib/api/route/settings.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@ Future<void> updateSettings(ApiConnection connection, {
99
for (final entry in newSettings.entries) {
1010
final name = entry.key;
1111
final valueRaw = entry.value;
12-
final value = switch (name) {
13-
UserSettingName.twentyFourHourTime => valueRaw as bool,
14-
UserSettingName.displayEmojiReactionUsers => valueRaw as bool,
15-
UserSettingName.emojiset => RawParameter((valueRaw as Emojiset).toJson()),
16-
UserSettingName.presenceEnabled => valueRaw as bool,
17-
};
12+
final Object? value;
13+
switch (name) {
14+
case UserSettingName.twentyFourHourTime:
15+
final mode = (valueRaw as TwentyFourHourTimeMode);
16+
// TODO(server-future) allow localeDefault for servers that support it
17+
assert(mode != TwentyFourHourTimeMode.localeDefault);
18+
value = mode.toJson();
19+
case UserSettingName.displayEmojiReactionUsers:
20+
value = valueRaw as bool;
21+
case UserSettingName.emojiset:
22+
value = RawParameter((valueRaw as Emojiset).toJson());
23+
case UserSettingName.presenceEnabled:
24+
value = valueRaw as bool;
25+
}
1826
params[name.toJson()] = value;
1927
}
2028

lib/model/store.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ class PerAccountStore extends PerAccountStoreBase with
707707
}
708708
switch (event.property!) {
709709
case UserSettingName.twentyFourHourTime:
710-
userSettings.twentyFourHourTime = event.value as bool;
710+
userSettings.twentyFourHourTime = event.value as TwentyFourHourTimeMode;
711711
case UserSettingName.displayEmojiReactionUsers:
712712
userSettings.displayEmojiReactionUsers = event.value as bool;
713713
case UserSettingName.emojiset:

test/api/route/settings_test.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ void main() {
1717
for (final name in UserSettingName.values) {
1818
switch (name) {
1919
case UserSettingName.twentyFourHourTime:
20-
newSettings[name] = true;
21-
expectedBodyFields['twenty_four_hour_time'] = 'true';
20+
newSettings[name] = TwentyFourHourTimeMode.twelveHour;
21+
expectedBodyFields['twenty_four_hour_time'] = 'false';
2222
case UserSettingName.displayEmojiReactionUsers:
2323
newSettings[name] = false;
2424
expectedBodyFields['display_emoji_reaction_users'] = 'false';
@@ -38,4 +38,16 @@ void main() {
3838
..bodyFields.deepEquals(expectedBodyFields);
3939
});
4040
});
41+
42+
test('TwentyFourHourTime.localeDefault', () async {
43+
return FakeApiConnection.with_((connection) async {
44+
connection.prepare(json: {});
45+
46+
// TODO(server-future) instead, check for twenty_four_hour_time: null
47+
// (could be an error-prone part of the JSONification)
48+
check(() => updateSettings(connection,
49+
newSettings: {UserSettingName.twentyFourHourTime: TwentyFourHourTimeMode.localeDefault})
50+
).throws<AssertionError>();
51+
});
52+
});
4153
}

test/example_data.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ InitialSnapshot initialSnapshot({
12621262
streams: streams ?? [], // TODO add streams to default
12631263
userStatuses: userStatuses ?? {},
12641264
userSettings: userSettings ?? UserSettings(
1265-
twentyFourHourTime: false,
1265+
twentyFourHourTime: TwentyFourHourTimeMode.twelveHour,
12661266
displayEmojiReactionUsers: true,
12671267
emojiset: Emojiset.google,
12681268
presenceEnabled: true,

test/model/store_test.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -698,14 +698,16 @@ void main() {
698698
await preparePoll();
699699

700700
// Pick some arbitrary event and check it gets processed on the store.
701-
check(store.userSettings.twentyFourHourTime).isFalse();
701+
check(store.userSettings.twentyFourHourTime)
702+
.equals(TwentyFourHourTimeMode.twelveHour);
702703
connection.prepare(json: GetEventsResult(events: [
703704
UserSettingsUpdateEvent(id: 2,
704705
property: UserSettingName.twentyFourHourTime, value: true),
705706
], queueId: null).toJson());
706707
updateMachine.debugAdvanceLoop();
707708
async.elapse(Duration.zero);
708-
check(store.userSettings.twentyFourHourTime).isTrue();
709+
check(store.userSettings.twentyFourHourTime)
710+
.equals(TwentyFourHourTimeMode.twentyFourHour);
709711
}));
710712

711713
void checkReload(FutureOr<void> Function() prepareError, {
@@ -735,14 +737,16 @@ void main() {
735737
// The new UpdateMachine updates the new store.
736738
updateMachine.debugPauseLoop();
737739
updateMachine.poll();
738-
check(store.userSettings.twentyFourHourTime).isFalse();
740+
check(store.userSettings.twentyFourHourTime)
741+
.equals(TwentyFourHourTimeMode.twelveHour);
739742
connection.prepare(json: GetEventsResult(events: [
740743
UserSettingsUpdateEvent(id: 2,
741744
property: UserSettingName.twentyFourHourTime, value: true),
742745
], queueId: null).toJson());
743746
updateMachine.debugAdvanceLoop();
744747
async.elapse(Duration.zero);
745-
check(store.userSettings.twentyFourHourTime).isTrue();
748+
check(store.userSettings.twentyFourHourTime)
749+
.equals(TwentyFourHourTimeMode.twentyFourHour);
746750
});
747751
}
748752

0 commit comments

Comments
 (0)