diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp index 7d21f182779c576969d0acdd1b310812f860170c..c37aa11d666a41cf2b9ef1574dc11ed6921cd4d3 100644 --- a/src/jamidht/conversation.cpp +++ b/src/jamidht/conversation.cpp @@ -1152,6 +1152,17 @@ Conversation::Impl::addToHistory(History& history, bool messageReceived, bool commitFromSelf) { + // + // NOTE: This function makes the following assumptions on its arguments: + // - The messages in "history" are in reverse chronological order (newest message + // first, oldest message last). + // - If messageReceived is true, then the commits in "commits" are assumed to be in + // chronological order (oldest to newest) and to be newer than the ones in "history". + // They are therefore inserted at the beginning of the message list. + // - If messageReceived is false, then the commits in "commits" are assumed to be in + // reverse chronological order (newest to oldest) and to be older than the ones in + // "history". They are therefore appended at the end of the message list. + // auto acc = account_.lock(); if (!acc) return {}; @@ -1161,6 +1172,9 @@ Conversation::Impl::addToHistory(History& history, history.cv.wait(lk, [&] { return !history.loading; }); } + // Only set messages' status on history for client + bool needToSetMessageStatus = !commitFromSelf && &history == &loadedHistory_; + std::vector<std::shared_ptr<libjami::SwarmMessage>> sharedCommits; for (const auto& commit : commits) { auto commitId = commit.at("id"); @@ -1173,68 +1187,81 @@ Conversation::Impl::addToHistory(History& history, auto sharedCommit = std::make_shared<libjami::SwarmMessage>(); sharedCommit->fromMapStringString(commit); + + if (needToSetMessageStatus) { + // Check if we already have status information for the commit. + auto itFuture = futureStatus.find(sharedCommit->id); + if (itFuture != futureStatus.end()) { + sharedCommit->status = std::move(itFuture->second); + futureStatus.erase(itFuture); + } + } + sharedCommits.emplace_back(sharedCommit); } - // Set message status based on cache (only on history for client) - if (!commitFromSelf && &history == &loadedHistory_) { - for (const auto& sharedCommit : sharedCommits) { - std::lock_guard lk(messageStatusMtx_); - for (const auto& member: repository_->members()) { - // If we have a status cached, use it - auto itFuture = futureStatus.find(sharedCommit->id); - if (itFuture != futureStatus.end()) { - sharedCommit->status = std::move(itFuture->second); - futureStatus.erase(itFuture); - continue; + if (needToSetMessageStatus) { + constexpr int32_t SENDING = static_cast<int32_t>(libjami::Account::MessageStates::SENDING); + constexpr int32_t SENT = static_cast<int32_t>(libjami::Account::MessageStates::SENT); + constexpr int32_t DISPLAYED = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); + + std::lock_guard lk(messageStatusMtx_); + for (const auto& member: repository_->members()) { + // For each member, we iterate over the commits to add in reverse chronological + // order (i.e. from newest to oldest) and set their status from the point of view + // of that member (as best we can given the information we have). + // + // The key assumption made in order to compute the status is that it can never decrease + // (with respect to the ordering SENDING < SENT < DISPLAYED) as we go back in time. We + // therefore start by setting the "status" variable below to the lowest possible value, + // and increase it when we encounter a commit for which it is justified to do so. + // + // If messageReceived is true, then the commits we are adding are the most recent in the + // conversation history, so the lowest possible value is SENDING. + // + // If messageReceived is false, then the commits we are adding are older than the ones + // that are already in the history, so the lowest possible value is the status of the + // oldest message in the history so far, which is stored in memberToStatus. + auto status = SENDING; + if (!messageReceived) { + auto cache = memberToStatus[member.uri]; + if (cache > status) + status = cache; + } + + for (auto it = sharedCommits.rbegin(); it != sharedCommits.rend(); it++) { + auto sharedCommit = *it; + auto previousStatus = status; + + // Compute status for the current commit. + if (status < SENT && messagesStatus_[member.uri]["fetched"] == sharedCommit->id) { + status = SENT; } - // Else we need to compute the status. - auto& cache = memberToStatus[member.uri]; - if (cache == 0) { - // Message is sending, sent or displayed - cache = static_cast<int32_t>(libjami::Account::MessageStates::SENDING); + if (messagesStatus_[member.uri]["read"] == sharedCommit->id) { + status = DISPLAYED; } - if (!messageReceived) { - // For loading previous messages, there is 3 cases. Last value cached is displayed, so is every previous commits - // Else, if last value is sent, we can compare to the last read commit to update the cache - // Finally if it's sending, we check last fetched commit - if (cache == static_cast<int32_t>(libjami::Account::MessageStates::SENT)) { - if (messagesStatus_[member.uri]["read"] == sharedCommit->id) { - cache = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); - } - } else if (cache <= static_cast<int32_t>(libjami::Account::MessageStates::SENDING)) { // SENDING or UNKNOWN - // cache can be upgraded to displayed or sent - if (messagesStatus_[member.uri]["read"] == sharedCommit->id) { - cache = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); - } else if (messagesStatus_[member.uri]["fetched"] == sharedCommit->id) { - cache = static_cast<int32_t>(libjami::Account::MessageStates::SENT); - } - } - if(static_cast<int32_t>(cache) > sharedCommit->status[member.uri]){ - sharedCommit->status[member.uri] = static_cast<int32_t>(cache); - } - } else { - // If member is author of the message received, they already saw it - auto author = sharedCommit->body.at("author"); - if (member.uri == author) { - // If member is the author of the commit, they are considered as displayed (same for all previous commits) - messagesStatus_[member.uri]["read"] = sharedCommit->id; - messagesStatus_[member.uri]["fetched"] = sharedCommit->id; - sharedCommit->status[author] = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); - cache = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); - continue; - } - // For receiving messages, every commit is considered as SENDING, unless we got a update - auto status = static_cast<int32_t>(libjami::Account::MessageStates::SENDING); - if (messagesStatus_[member.uri]["read"] == sharedCommit->id) { - status = static_cast<int32_t>(libjami::Account::MessageStates::DISPLAYED); - } else if (messagesStatus_[member.uri]["fetched"] == sharedCommit->id) { - status = static_cast<int32_t>(libjami::Account::MessageStates::SENT); - } - if(static_cast<int32_t>(status) > sharedCommit->status[member.uri]){ - sharedCommit->status[member.uri] = static_cast<int32_t>(status); - } + if (member.uri == sharedCommit->body.at("author")) { + status = DISPLAYED; + } + if(status < sharedCommit->status[member.uri]){ + status = sharedCommit->status[member.uri]; } + + // Store computed value. + sharedCommit->status[member.uri] = status; + + // Update messagesStatus_ if needed. + if (previousStatus == SENDING && status >= SENT) { + messagesStatus_[member.uri]["fetched"] = sharedCommit->id; + } + if (previousStatus <= SENT && status == DISPLAYED) { + messagesStatus_[member.uri]["read"] = sharedCommit->id; + } + } + + if (!messageReceived) { + // Update memberToStatus with the status of the last (i.e. oldest) added commit. + memberToStatus[member.uri] = status; } } }