ensureQuickCommentsLoaded method
批量加载消息表情快捷回复,已加载和加载中的消息不会重复请求
Implementation
Future<void> ensureQuickCommentsLoaded(
Iterable<ChatMessage> messages,
) async {
if (!IMKitConfigCenter.enableMessageReaction || _isDisposed) {
return;
}
final candidates = <String, ChatMessage>{};
for (final message in messages) {
if (!MessageReactionHelper.isSupported(message)) {
continue;
}
final identity =
MessageReactionHelper.messageIdentity(message.nimMessage);
if (identity == null) {
continue;
}
final state = _messageReactionStates[identity];
if (state?.loaded == true || state?.loading == true) {
continue;
}
candidates[identity] = message;
}
if (candidates.isEmpty) {
return;
}
final requestVersions = <String, int>{};
for (final identity in candidates.keys) {
final current =
_messageReactionStates[identity] ?? MessageReactionState.empty;
final version = current.version + 1;
requestVersions[identity] = version;
_messageReactionStates[identity] = current.copyWith(
loading: true,
version: version,
);
}
notifyListeners();
final lifecycleVersion = _reactionLifecycleVersion;
NIMResult<Map<String, List<NIMMessageQuickComment>?>>? failedResult;
final quickComments = <String, List<NIMMessageQuickComment>?>{};
try {
final candidateMessages =
candidates.values.map((message) => message.nimMessage).toList();
for (var offset = 0;
offset < candidateMessages.length;
offset += _quickCommentBatchSize) {
final end = min(
offset + _quickCommentBatchSize,
candidateMessages.length,
);
final batch = candidateMessages.sublist(offset, end);
final batchResult = await (_quickCommentListLoader?.call(batch) ??
ChatMessageRepo.getQuickCommentList(batch));
if (!batchResult.isSuccess) {
failedResult = batchResult;
break;
}
quickComments.addAll(
batchResult.data ?? const <String, List<NIMMessageQuickComment>?>{},
);
}
} catch (error) {
if (_isDisposed || lifecycleVersion != _reactionLifecycleVersion) {
return;
}
for (final entry in requestVersions.entries) {
final current = _messageReactionStates[entry.key];
if (current?.version == entry.value) {
_messageReactionStates[entry.key] = current!.copyWith(
loading: false,
);
}
}
_logI('getQuickCommentList threw: $error');
notifyListeners();
return;
}
if (_isDisposed || lifecycleVersion != _reactionLifecycleVersion) {
return;
}
if (failedResult != null) {
for (final entry in requestVersions.entries) {
final current = _messageReactionStates[entry.key];
if (current?.version == entry.value) {
_messageReactionStates[entry.key] = current!.copyWith(
loading: false,
);
}
}
_logI(
'getQuickCommentList failed, code=${failedResult.code}, error=${failedResult.errorDetails}',
);
notifyListeners();
return;
}
final candidateIdentitiesBySdkKey = <String, String>{};
for (final entry in candidates.entries) {
final message = entry.value.nimMessage;
final clientId = message.messageClientId;
if (clientId?.isNotEmpty == true) {
candidateIdentitiesBySdkKey[clientId!] = entry.key;
}
final serverId = message.messageServerId;
if (serverId?.isNotEmpty == true && serverId != '-1') {
candidateIdentitiesBySdkKey[serverId!] = entry.key;
}
}
final grouped = <String, Map<int, Map<String, int>>>{};
for (final resultEntry in quickComments.entries) {
final sdkKeyIdentity = candidateIdentitiesBySdkKey[resultEntry.key];
for (final comment
in resultEntry.value ?? const <NIMMessageQuickComment>[]) {
final refer = comment.messageRefer;
final referIdentity =
refer == null ? null : MessageReactionHelper.messageIdentity(refer);
final identity =
referIdentity != null && candidates.containsKey(referIdentity)
? referIdentity
: sdkKeyIdentity;
final index = comment.index;
final operatorId = comment.operatorId;
if (identity == null ||
!candidates.containsKey(identity) ||
index == null ||
!MessageReactionConfig.supportedIndexes.contains(index) ||
operatorId?.isNotEmpty != true) {
continue;
}
final times = grouped
.putIfAbsent(identity, () => <int, Map<String, int>>{})
.putIfAbsent(index, () => <String, int>{});
final time = comment.createTime ?? 0;
final previous = times[operatorId];
if (previous == null ||
previous <= 0 ||
(time > 0 && time < previous)) {
times[operatorId!] = time;
}
}
}
for (final entry in candidates.entries) {
final current = _messageReactionStates[entry.key];
if (current == null) {
continue;
}
final summaries = <int, MessageReactionSummary>{};
for (final summary in grouped[entry.key]?.entries ??
const <MapEntry<int, Map<String, int>>>[]) {
summaries[summary.key] = MessageReactionSummary(
index: summary.key,
operatorIds: summary.value.keys,
operatorCreateTimes: summary.value,
);
}
for (final overrideEntry in _reactionOverrides[entry.key]?.entries ??
const <MapEntry<int, Map<String, bool>>>[]) {
final oldSummary = summaries[overrideEntry.key];
final operators = Set<String>.from(
oldSummary?.operatorIds ?? const <String>{},
);
final times = Map<String, int>.from(
oldSummary?.operatorCreateTimes ?? const <String, int>{},
);
for (final operatorOverride in overrideEntry.value.entries) {
operatorOverride.value
? operators.add(operatorOverride.key)
: operators.remove(operatorOverride.key);
if (operatorOverride.value) {
final time = current.summaries[overrideEntry.key]
?.operatorCreateTimes[operatorOverride.key];
if (time != null) times[operatorOverride.key] = time;
}
}
if (operators.isEmpty) {
summaries.remove(overrideEntry.key);
} else {
summaries[overrideEntry.key] = MessageReactionSummary(
index: overrideEntry.key,
operatorIds: operators,
operatorCreateTimes: times,
pending: current.summaries[overrideEntry.key]?.pending ?? false,
);
}
}
_messageReactionStates[entry.key] = MessageReactionState(
summaries: summaries,
loaded: true,
version: current.version + 1,
);
}
notifyListeners();
}