hardened API decoding against server fields the models did not expect
The Talk room list crashed for a new account because the server omits
lastMessage for rooms without a visible message and the model required it.
Beyond that single field, a review of the Talk models against the OpenAPI
spec of the installed version (23.0.8) found the same crash class in several
places: closed enums over open server values (inCall bitmask, rich-object
types, actor and message types), a deleted parent sent as {id, deleted},
and the autocomplete status arriving as an object.
- json_serializable `checked: true`: decode errors now name class and field
- lists (rooms, messages, participants, autocomplete) decode per entry;
a malformed entry is skipped and reported via ClientErrorReporter
- unknownEnumValue fallbacks for all response enums of Talk and breakers
- lastMessage nullable; inCall as bitmask; deleted parent placeholder;
autocomplete status object/string; fixed shareWithDisplayNameUnique key
- GetRoomParams no longer sends null query parameters
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HrepDTL4qDe35TuS29sLW3
This commit is contained in:
@@ -8,14 +8,23 @@ part of 'chat_background_settings.dart';
|
||||
|
||||
ChatBackgroundSettings _$ChatBackgroundSettingsFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => ChatBackgroundSettings(
|
||||
type: $enumDecode(_$ChatBackgroundTypeEnumMap, json['type']),
|
||||
fit: $enumDecode(_$ChatBackgroundFitEnumMap, json['fit']),
|
||||
colorValue: (json['colorValue'] as num?)?.toInt(),
|
||||
imageVersion: (json['imageVersion'] as num).toInt(),
|
||||
dim: (json['dim'] as num).toDouble(),
|
||||
blur: (json['blur'] as num).toDouble(),
|
||||
);
|
||||
) => $checkedCreate('ChatBackgroundSettings', json, ($checkedConvert) {
|
||||
final val = ChatBackgroundSettings(
|
||||
type: $checkedConvert(
|
||||
'type',
|
||||
(v) => $enumDecode(_$ChatBackgroundTypeEnumMap, v),
|
||||
),
|
||||
fit: $checkedConvert(
|
||||
'fit',
|
||||
(v) => $enumDecode(_$ChatBackgroundFitEnumMap, v),
|
||||
),
|
||||
colorValue: $checkedConvert('colorValue', (v) => (v as num?)?.toInt()),
|
||||
imageVersion: $checkedConvert('imageVersion', (v) => (v as num).toInt()),
|
||||
dim: $checkedConvert('dim', (v) => (v as num).toDouble()),
|
||||
blur: $checkedConvert('blur', (v) => (v as num).toDouble()),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$ChatBackgroundSettingsToJson(
|
||||
ChatBackgroundSettings instance,
|
||||
|
||||
@@ -7,23 +7,49 @@ part of 'dev_tools_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
DevToolsSettings _$DevToolsSettingsFromJson(Map<String, dynamic> json) =>
|
||||
DevToolsSettings(
|
||||
showPerformanceOverlay: json['showPerformanceOverlay'] as bool,
|
||||
showFrameStats: json['showFrameStats'] as bool? ?? false,
|
||||
showMemoryStats: json['showMemoryStats'] as bool? ?? false,
|
||||
showSemanticsDebugger: json['showSemanticsDebugger'] as bool? ?? false,
|
||||
slowAnimations: json['slowAnimations'] as bool? ?? false,
|
||||
textScaleOverride: (json['textScaleOverride'] as num?)?.toDouble() ?? 0,
|
||||
marianumConnectEndpoint:
|
||||
$enumDecodeNullable(
|
||||
_$MarianumConnectEndpointEnumMap,
|
||||
json['marianumConnectEndpoint'],
|
||||
) ??
|
||||
MarianumConnectEndpoint.live,
|
||||
marianumConnectCustomUrl:
|
||||
json['marianumConnectCustomUrl'] as String? ?? '',
|
||||
emergencyNoticeUrl: json['emergencyNoticeUrl'] as String? ?? '',
|
||||
);
|
||||
$checkedCreate('DevToolsSettings', json, ($checkedConvert) {
|
||||
final val = DevToolsSettings(
|
||||
showPerformanceOverlay: $checkedConvert(
|
||||
'showPerformanceOverlay',
|
||||
(v) => v as bool,
|
||||
),
|
||||
showFrameStats: $checkedConvert(
|
||||
'showFrameStats',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
showMemoryStats: $checkedConvert(
|
||||
'showMemoryStats',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
showSemanticsDebugger: $checkedConvert(
|
||||
'showSemanticsDebugger',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
slowAnimations: $checkedConvert(
|
||||
'slowAnimations',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
textScaleOverride: $checkedConvert(
|
||||
'textScaleOverride',
|
||||
(v) => (v as num?)?.toDouble() ?? 0,
|
||||
),
|
||||
marianumConnectEndpoint: $checkedConvert(
|
||||
'marianumConnectEndpoint',
|
||||
(v) =>
|
||||
$enumDecodeNullable(_$MarianumConnectEndpointEnumMap, v) ??
|
||||
MarianumConnectEndpoint.live,
|
||||
),
|
||||
marianumConnectCustomUrl: $checkedConvert(
|
||||
'marianumConnectCustomUrl',
|
||||
(v) => v as String? ?? '',
|
||||
),
|
||||
emergencyNoticeUrl: $checkedConvert(
|
||||
'emergencyNoticeUrl',
|
||||
(v) => v as String? ?? '',
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$DevToolsSettingsToJson(DevToolsSettings instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -6,11 +6,18 @@ part of 'file_settings.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
FileSettings _$FileSettingsFromJson(Map<String, dynamic> json) => FileSettings(
|
||||
sortFoldersToTop: json['sortFoldersToTop'] as bool,
|
||||
ascending: json['ascending'] as bool,
|
||||
sortBy: $enumDecode(_$SortOptionEnumMap, json['sortBy']),
|
||||
);
|
||||
FileSettings _$FileSettingsFromJson(Map<String, dynamic> json) =>
|
||||
$checkedCreate('FileSettings', json, ($checkedConvert) {
|
||||
final val = FileSettings(
|
||||
sortFoldersToTop: $checkedConvert('sortFoldersToTop', (v) => v as bool),
|
||||
ascending: $checkedConvert('ascending', (v) => v as bool),
|
||||
sortBy: $checkedConvert(
|
||||
'sortBy',
|
||||
(v) => $enumDecode(_$SortOptionEnumMap, v),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$FileSettingsToJson(FileSettings instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -7,9 +7,15 @@ part of 'file_view_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
FileViewSettings _$FileViewSettingsFromJson(Map<String, dynamic> json) =>
|
||||
FileViewSettings(
|
||||
alwaysOpenExternally: json['alwaysOpenExternally'] as bool,
|
||||
);
|
||||
$checkedCreate('FileViewSettings', json, ($checkedConvert) {
|
||||
final val = FileViewSettings(
|
||||
alwaysOpenExternally: $checkedConvert(
|
||||
'alwaysOpenExternally',
|
||||
(v) => v as bool,
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$FileViewSettingsToJson(FileViewSettings instance) =>
|
||||
<String, dynamic>{'alwaysOpenExternally': instance.alwaysOpenExternally};
|
||||
|
||||
@@ -7,7 +7,15 @@ part of 'haptic_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
HapticSettings _$HapticSettingsFromJson(Map<String, dynamic> json) =>
|
||||
HapticSettings(level: $enumDecode(_$HapticLevelEnumMap, json['level']));
|
||||
$checkedCreate('HapticSettings', json, ($checkedConvert) {
|
||||
final val = HapticSettings(
|
||||
level: $checkedConvert(
|
||||
'level',
|
||||
(v) => $enumDecode(_$HapticLevelEnumMap, v),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$HapticSettingsToJson(HapticSettings instance) =>
|
||||
<String, dynamic>{'level': _$HapticLevelEnumMap[instance.level]!};
|
||||
|
||||
@@ -7,10 +7,16 @@ part of 'holidays_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
HolidaysSettings _$HolidaysSettingsFromJson(Map<String, dynamic> json) =>
|
||||
HolidaysSettings(
|
||||
dismissedDisclaimer: json['dismissedDisclaimer'] as bool,
|
||||
showPastEvents: json['showPastEvents'] as bool,
|
||||
);
|
||||
$checkedCreate('HolidaysSettings', json, ($checkedConvert) {
|
||||
final val = HolidaysSettings(
|
||||
dismissedDisclaimer: $checkedConvert(
|
||||
'dismissedDisclaimer',
|
||||
(v) => v as bool,
|
||||
),
|
||||
showPastEvents: $checkedConvert('showPastEvents', (v) => v as bool),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$HolidaysSettingsToJson(HolidaysSettings instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -7,17 +7,35 @@ part of 'modules_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
ModulesSettings _$ModulesSettingsFromJson(Map<String, dynamic> json) =>
|
||||
ModulesSettings(
|
||||
moduleOrder: (json['moduleOrder'] as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
hiddenModules: (json['hiddenModules'] as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
autoFillBottomBar: json['autoFillBottomBar'] as bool? ?? true,
|
||||
fixedBottomBarSlots: (json['fixedBottomBarSlots'] as num?)?.toInt() ?? 3,
|
||||
teacherDefaultsApplied: json['teacherDefaultsApplied'] as bool? ?? false,
|
||||
);
|
||||
$checkedCreate('ModulesSettings', json, ($checkedConvert) {
|
||||
final val = ModulesSettings(
|
||||
moduleOrder: $checkedConvert(
|
||||
'moduleOrder',
|
||||
(v) => (v as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
),
|
||||
hiddenModules: $checkedConvert(
|
||||
'hiddenModules',
|
||||
(v) => (v as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
),
|
||||
autoFillBottomBar: $checkedConvert(
|
||||
'autoFillBottomBar',
|
||||
(v) => v as bool? ?? true,
|
||||
),
|
||||
fixedBottomBarSlots: $checkedConvert(
|
||||
'fixedBottomBarSlots',
|
||||
(v) => (v as num?)?.toInt() ?? 3,
|
||||
),
|
||||
teacherDefaultsApplied: $checkedConvert(
|
||||
'teacherDefaultsApplied',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$ModulesSettingsToJson(
|
||||
ModulesSettings instance,
|
||||
|
||||
@@ -8,11 +8,16 @@ part of 'notification_settings.dart';
|
||||
|
||||
NotificationSettings _$NotificationSettingsFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => NotificationSettings(
|
||||
enabled: json['enabled'] as bool? ?? true,
|
||||
talkPermissionPromptShown:
|
||||
json['talkPermissionPromptShown'] as bool? ?? false,
|
||||
);
|
||||
) => $checkedCreate('NotificationSettings', json, ($checkedConvert) {
|
||||
final val = NotificationSettings(
|
||||
enabled: $checkedConvert('enabled', (v) => v as bool? ?? true),
|
||||
talkPermissionPromptShown: $checkedConvert(
|
||||
'talkPermissionPromptShown',
|
||||
(v) => v as bool? ?? false,
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$NotificationSettingsToJson(
|
||||
NotificationSettings instance,
|
||||
|
||||
+55
-37
@@ -6,43 +6,61 @@ part of 'settings.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
||||
appTheme: Settings._themeFromJson(json['appTheme'] as String),
|
||||
devToolsEnabled: json['devToolsEnabled'] as bool,
|
||||
modulesSettings: ModulesSettings.fromJson(
|
||||
json['modulesSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
timetableSettings: TimetableSettings.fromJson(
|
||||
json['timetableSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
timetableFavoritesSettings: TimetableFavoritesSettings.fromJson(
|
||||
json['timetableFavoritesSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
talkSettings: TalkSettings.fromJson(
|
||||
json['talkSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
chatBackgroundSettings: ChatBackgroundSettings.fromJson(
|
||||
json['chatBackgroundSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
fileSettings: FileSettings.fromJson(
|
||||
json['fileSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
holidaysSettings: HolidaysSettings.fromJson(
|
||||
json['holidaysSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
fileViewSettings: FileViewSettings.fromJson(
|
||||
json['fileViewSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
notificationSettings: NotificationSettings.fromJson(
|
||||
json['notificationSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
devToolsSettings: DevToolsSettings.fromJson(
|
||||
json['devToolsSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
hapticSettings: HapticSettings.fromJson(
|
||||
json['hapticSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
Settings _$SettingsFromJson(Map<String, dynamic> json) =>
|
||||
$checkedCreate('Settings', json, ($checkedConvert) {
|
||||
final val = Settings(
|
||||
appTheme: $checkedConvert(
|
||||
'appTheme',
|
||||
(v) => Settings._themeFromJson(v as String),
|
||||
),
|
||||
devToolsEnabled: $checkedConvert('devToolsEnabled', (v) => v as bool),
|
||||
modulesSettings: $checkedConvert(
|
||||
'modulesSettings',
|
||||
(v) => ModulesSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
timetableSettings: $checkedConvert(
|
||||
'timetableSettings',
|
||||
(v) => TimetableSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
timetableFavoritesSettings: $checkedConvert(
|
||||
'timetableFavoritesSettings',
|
||||
(v) => TimetableFavoritesSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
talkSettings: $checkedConvert(
|
||||
'talkSettings',
|
||||
(v) => TalkSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
chatBackgroundSettings: $checkedConvert(
|
||||
'chatBackgroundSettings',
|
||||
(v) => ChatBackgroundSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
fileSettings: $checkedConvert(
|
||||
'fileSettings',
|
||||
(v) => FileSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
holidaysSettings: $checkedConvert(
|
||||
'holidaysSettings',
|
||||
(v) => HolidaysSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
fileViewSettings: $checkedConvert(
|
||||
'fileViewSettings',
|
||||
(v) => FileViewSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
notificationSettings: $checkedConvert(
|
||||
'notificationSettings',
|
||||
(v) => NotificationSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
devToolsSettings: $checkedConvert(
|
||||
'devToolsSettings',
|
||||
(v) => DevToolsSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
hapticSettings: $checkedConvert(
|
||||
'hapticSettings',
|
||||
(v) => HapticSettings.fromJson(v as Map<String, dynamic>),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||
'appTheme': Settings._themeToJson(instance.appTheme),
|
||||
|
||||
@@ -6,12 +6,25 @@ part of 'talk_settings.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
TalkSettings _$TalkSettingsFromJson(Map<String, dynamic> json) => TalkSettings(
|
||||
sortFavoritesToTop: json['sortFavoritesToTop'] as bool,
|
||||
sortUnreadToTop: json['sortUnreadToTop'] as bool,
|
||||
drafts: Map<String, String>.from(json['drafts'] as Map),
|
||||
draftReplies: Map<String, int>.from(json['draftReplies'] as Map),
|
||||
);
|
||||
TalkSettings _$TalkSettingsFromJson(Map<String, dynamic> json) =>
|
||||
$checkedCreate('TalkSettings', json, ($checkedConvert) {
|
||||
final val = TalkSettings(
|
||||
sortFavoritesToTop: $checkedConvert(
|
||||
'sortFavoritesToTop',
|
||||
(v) => v as bool,
|
||||
),
|
||||
sortUnreadToTop: $checkedConvert('sortUnreadToTop', (v) => v as bool),
|
||||
drafts: $checkedConvert(
|
||||
'drafts',
|
||||
(v) => Map<String, String>.from(v as Map),
|
||||
),
|
||||
draftReplies: $checkedConvert(
|
||||
'draftReplies',
|
||||
(v) => Map<String, int>.from(v as Map),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$TalkSettingsToJson(TalkSettings instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -8,11 +8,17 @@ part of 'timetable_favorites_settings.dart';
|
||||
|
||||
FavoriteTimetableElement _$FavoriteTimetableElementFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => FavoriteTimetableElement(
|
||||
type: $enumDecode(_$TimetableElementTypeEnumMap, json['type']),
|
||||
id: (json['id'] as num).toInt(),
|
||||
label: json['label'] as String,
|
||||
);
|
||||
) => $checkedCreate('FavoriteTimetableElement', json, ($checkedConvert) {
|
||||
final val = FavoriteTimetableElement(
|
||||
type: $checkedConvert(
|
||||
'type',
|
||||
(v) => $enumDecode(_$TimetableElementTypeEnumMap, v),
|
||||
),
|
||||
id: $checkedConvert('id', (v) => (v as num).toInt()),
|
||||
label: $checkedConvert('label', (v) => v as String),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$FavoriteTimetableElementToJson(
|
||||
FavoriteTimetableElement instance,
|
||||
@@ -31,11 +37,19 @@ const _$TimetableElementTypeEnumMap = {
|
||||
|
||||
TimetableFavoritesSettings _$TimetableFavoritesSettingsFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => TimetableFavoritesSettings(
|
||||
favorites: (json['favorites'] as List<dynamic>)
|
||||
.map((e) => FavoriteTimetableElement.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
) => $checkedCreate('TimetableFavoritesSettings', json, ($checkedConvert) {
|
||||
final val = TimetableFavoritesSettings(
|
||||
favorites: $checkedConvert(
|
||||
'favorites',
|
||||
(v) => (v as List<dynamic>)
|
||||
.map(
|
||||
(e) => FavoriteTimetableElement.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$TimetableFavoritesSettingsToJson(
|
||||
TimetableFavoritesSettings instance,
|
||||
|
||||
@@ -7,13 +7,19 @@ part of 'timetable_settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
TimetableSettings _$TimetableSettingsFromJson(Map<String, dynamic> json) =>
|
||||
TimetableSettings(
|
||||
connectDoubleLessons: json['connectDoubleLessons'] as bool,
|
||||
timetableNameMode: $enumDecode(
|
||||
_$TimetableNameModeEnumMap,
|
||||
json['timetableNameMode'],
|
||||
),
|
||||
);
|
||||
$checkedCreate('TimetableSettings', json, ($checkedConvert) {
|
||||
final val = TimetableSettings(
|
||||
connectDoubleLessons: $checkedConvert(
|
||||
'connectDoubleLessons',
|
||||
(v) => v as bool,
|
||||
),
|
||||
timetableNameMode: $checkedConvert(
|
||||
'timetableNameMode',
|
||||
(v) => $enumDecode(_$TimetableNameModeEnumMap, v),
|
||||
),
|
||||
);
|
||||
return val;
|
||||
});
|
||||
|
||||
Map<String, dynamic> _$TimetableSettingsToJson(
|
||||
TimetableSettings instance,
|
||||
|
||||
Reference in New Issue
Block a user