Files
Client/lib/api/marianumcloud/search/search_chat_messages_response.dart
T

95 lines
3.0 KiB
Dart

/// Parsed page of Talk's `talk-message-current` search provider.
class SearchChatMessagesResponse {
final List<SearchChatMessagesEntry> entries;
/// Opaque pagination cursor; `null` when the provider has no further page.
final Object? cursor;
const SearchChatMessagesResponse({required this.entries, this.cursor});
factory SearchChatMessagesResponse.fromJson(
Map<String, dynamic> json, {
required String term,
}) {
final rawEntries = json['entries'];
final entries = rawEntries is List
? rawEntries
.whereType<Map<String, dynamic>>()
.map((e) => SearchChatMessagesEntry.fromJson(e, term: term))
.whereType<SearchChatMessagesEntry>()
.toList()
: const <SearchChatMessagesEntry>[];
final paginated = json['isPaginated'] == true;
final cursor = json['cursor'];
return SearchChatMessagesResponse(
entries: entries,
cursor: paginated && entries.isNotEmpty && cursor != null ? cursor : null,
);
}
}
class SearchChatMessagesEntry {
final int messageId;
final String? actorId;
final String? actorDisplayName;
/// Server-built excerpt of the message, placeholders already resolved.
final String excerpt;
final DateTime? sentAt;
const SearchChatMessagesEntry({
required this.messageId,
required this.excerpt,
this.actorId,
this.actorDisplayName,
this.sentAt,
});
/// Returns `null` for entries without a recognisable message id.
static SearchChatMessagesEntry? fromJson(
Map<String, dynamic> json, {
required String term,
}) {
final attributes = json['attributes'];
final attrs = attributes is Map ? attributes : const {};
final messageId =
_parseInt(attrs['messageId']) ??
_parseInt(
RegExp(
r'#message_(\d+)',
).firstMatch(json['resourceUrl']?.toString() ?? '')?.group(1),
);
if (messageId == null) return null;
// Title and subline have swapped roles across Talk versions (author vs.
// excerpt); the one containing the search term is the excerpt.
final title = json['title']?.toString() ?? '';
final subline = json['subline']?.toString() ?? '';
final q = term.trim().toLowerCase();
final titleIsExcerpt =
q.isNotEmpty &&
title.toLowerCase().contains(q) &&
!subline.toLowerCase().contains(q);
final excerpt = titleIsExcerpt ? title : subline;
final author = titleIsExcerpt ? subline : title;
final timestamp = _parseInt(attrs['timestamp']);
final actorId = attrs['actorId']?.toString();
return SearchChatMessagesEntry(
messageId: messageId,
excerpt: excerpt,
actorId: actorId == null || actorId.isEmpty ? null : actorId,
actorDisplayName: author.isEmpty ? null : author,
sentAt: timestamp == null
? null
: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
);
}
static int? _parseInt(Object? raw) => switch (raw) {
final int v => v,
final String v => int.tryParse(v),
_ => null,
};
}