Files
Client/lib/api/emergency/emergency_notice.dart
T

91 lines
2.5 KiB
Dart

/// A backend-independent emergency notice loaded from a foreign server URL —
/// frontmatter (control fields) plus a free Markdown body, so it stays
/// hand-writable in an outage. See [parse] for the format.
class EmergencyNotice {
/// `false` renders full-screen and blocks back/barrier taps.
final bool dismissible;
final String? title;
final String body;
const EmergencyNotice({
required this.dismissible,
required this.title,
required this.body,
});
/// Parses the raw file, or returns `null` when there is nothing to show.
/// Never throws — malformed input yields `null` so a broken file can't break
/// the app.
///
/// ```
/// ---
/// active: true # required truthy, else null; # lines are comments
/// dismissible: true # default true
/// title: Störung # optional
/// ---
/// Free **markdown** body (everything after the closing ---).
/// ```
static EmergencyNotice? parse(String raw) {
final lines = raw
.replaceAll('\r\n', '\n')
.replaceAll('\r', '\n')
.split('\n');
var i = 0;
while (i < lines.length && lines[i].trim().isEmpty) {
i++;
}
if (i >= lines.length || lines[i].trim() != '---') return null;
final openIndex = i;
var closeIndex = -1;
for (var j = openIndex + 1; j < lines.length; j++) {
if (lines[j].trim() == '---') {
closeIndex = j;
break;
}
}
if (closeIndex == -1) return null;
final meta = <String, String>{};
for (var j = openIndex + 1; j < closeIndex; j++) {
final line = lines[j].trim();
if (line.isEmpty || line.startsWith('#')) continue;
final sep = line.indexOf(':');
if (sep <= 0) continue;
final key = line.substring(0, sep).trim().toLowerCase();
final value = line.substring(sep + 1).trim();
meta[key] = value;
}
if (_parseBool(meta['active']) != true) return null;
final body = lines.sublist(closeIndex + 1).join('\n').trim();
if (body.isEmpty) return null;
final title = meta['title'];
return EmergencyNotice(
dismissible: _parseBool(meta['dismissible']) ?? true,
title: (title == null || title.isEmpty) ? null : title,
body: body,
);
}
static bool? _parseBool(String? value) {
switch (value?.trim().toLowerCase()) {
case 'true':
case 'yes':
case '1':
case 'on':
return true;
case 'false':
case 'no':
case '0':
case 'off':
return false;
default:
return null;
}
}
}