ensureQuickCommentsLoaded method

Future<void> ensureQuickCommentsLoaded(
  1. Iterable<ChatMessage> messages
)

批量加载消息表情快捷回复,已加载和加载中的消息不会重复请求

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();
}