mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-07-25 04:51:52 +00:00
perf: reduce chat list and message handling latency
This commit is contained in:
@@ -976,6 +976,7 @@ class MinimalAppFacade final : public app::IAppFacade
|
||||
if (chat_service_)
|
||||
{
|
||||
chat_service_->processIncoming();
|
||||
chat_service_->flushStore();
|
||||
}
|
||||
|
||||
if (team_pairing_)
|
||||
|
||||
@@ -615,6 +615,7 @@ void AppFacadeRuntime::updateCoreServices()
|
||||
if (chat_service_)
|
||||
{
|
||||
chat_service_->processIncoming();
|
||||
chat_service_->flushStore();
|
||||
}
|
||||
if (ble_manager_)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,13 @@ class IChatStore
|
||||
* @return true if updated
|
||||
*/
|
||||
virtual bool updateMessageStatus(MessageId msg_id, MessageStatus status) = 0;
|
||||
|
||||
/**
|
||||
* @brief Flush pending buffered writes to persistent storage
|
||||
*
|
||||
* Default implementation is a no-op for stores that do not buffer.
|
||||
*/
|
||||
virtual void flush() {}
|
||||
};
|
||||
|
||||
} // namespace chat
|
||||
|
||||
@@ -96,6 +96,7 @@ class ChatService
|
||||
* @brief Process incoming messages (call from mesh task)
|
||||
*/
|
||||
void processIncoming();
|
||||
void flushStore();
|
||||
|
||||
void addIncomingTextObserver(IncomingTextObserver* observer);
|
||||
void removeIncomingTextObserver(IncomingTextObserver* observer);
|
||||
|
||||
@@ -202,6 +202,11 @@ void ChatService::processIncoming()
|
||||
}
|
||||
}
|
||||
|
||||
void ChatService::flushStore()
|
||||
{
|
||||
store_.flush();
|
||||
}
|
||||
|
||||
void ChatService::addIncomingMessageObserver(IncomingMessageObserver* observer)
|
||||
{
|
||||
if (!observer)
|
||||
|
||||
@@ -33,6 +33,7 @@ class ChatConversationScreen
|
||||
void addMessage(const chat::ChatMessage& msg);
|
||||
void clearMessages();
|
||||
void scrollToBottom();
|
||||
bool updateMessageStatus(chat::MessageId msg_id, chat::MessageStatus status);
|
||||
|
||||
void setActionCallback(void (*cb)(ActionIntent intent, void*), void* user_data);
|
||||
bool isAlive() const { return guard_ && guard_->alive; }
|
||||
|
||||
@@ -140,6 +140,8 @@ class ChatMessageListScreen
|
||||
message_list::input::Controller input_controller_{};
|
||||
|
||||
void rebuildList();
|
||||
bool updateListInPlace(const std::vector<chat::ConversationMeta>& convs);
|
||||
void updateListItem(size_t index, const chat::ConversationMeta& conv);
|
||||
void updateFilterHighlight();
|
||||
void setFilterMode(FilterMode mode);
|
||||
|
||||
|
||||
@@ -75,8 +75,14 @@ class UiController : public IChatUiRuntime
|
||||
void handleChannelSelected(const chat::ConversationId& conv);
|
||||
void handleSendMessage(const std::string& text);
|
||||
void refreshUnreadCounts();
|
||||
void refreshUnreadCounts(bool force_reload);
|
||||
void cleanupComposeIme();
|
||||
bool isTeamConversation(const chat::ConversationId& conv) const;
|
||||
void syncConversationListFromStore();
|
||||
void normalizeConversationNames(std::vector<chat::ConversationMeta>& convs) const;
|
||||
void applyConversationListToUi();
|
||||
void updateConversationMetaForMessage(const chat::ChatMessage& msg, bool increment_unread);
|
||||
bool updateConversationViewForIncoming(const chat::ChatMessage& msg);
|
||||
void refreshTeamConversation();
|
||||
void startTeamConversationTimer();
|
||||
void stopTeamConversationTimer();
|
||||
@@ -121,6 +127,8 @@ class UiController : public IChatUiRuntime
|
||||
uint64_t key_verify_nonce_ = 0;
|
||||
bool key_verify_expects_number_ = false;
|
||||
bool key_verify_can_trust_ = false;
|
||||
std::vector<chat::ConversationMeta> cached_conversations_;
|
||||
bool conversation_list_dirty_ = true;
|
||||
|
||||
static void team_position_icon_event_cb(lv_event_t* e);
|
||||
static void team_position_cancel_event_cb(lv_event_t* e);
|
||||
|
||||
@@ -344,6 +344,44 @@ void ChatConversationScreen::scrollToBottom()
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatConversationScreen::updateMessageStatus(const chat::MessageId msg_id,
|
||||
const chat::MessageStatus status)
|
||||
{
|
||||
if (!guard_ || !guard_->alive || msg_id == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& item : messages_)
|
||||
{
|
||||
if (item.msg.msg_id != msg_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.msg.status = status;
|
||||
if (!item.status_label)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status == MessageStatus::Failed)
|
||||
{
|
||||
lv_label_set_text(item.status_label, "Failed");
|
||||
::ui::fonts::apply_ui_chrome_font(item.status_label);
|
||||
lv_obj_clear_flag(item.status_label, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(item.status_label, "");
|
||||
::ui::fonts::apply_ui_chrome_font(item.status_label);
|
||||
lv_obj_add_flag(item.status_label, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChatConversationScreen::setActionCallback(void (*cb)(ActionIntent intent, void*), void* user_data)
|
||||
{
|
||||
if (!guard_ || !guard_->alive)
|
||||
|
||||
@@ -156,6 +156,23 @@ static bool conversation_list_equal(const std::vector<chat::ConversationMeta>& l
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool conversation_identity_list_equal(const std::vector<chat::ConversationMeta>& lhs,
|
||||
const std::vector<chat::ConversationMeta>& rhs)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (size_t index = 0; index < lhs.size(); ++index)
|
||||
{
|
||||
if (!(lhs[index].id == rhs[index].id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* touch_event_name(lv_event_code_t code)
|
||||
{
|
||||
switch (code)
|
||||
@@ -337,6 +354,7 @@ void ChatMessageListScreen::setConversations(const std::vector<chat::Conversatio
|
||||
team_visibility_changed ? 1 : 0,
|
||||
(unsigned)convs.size());
|
||||
|
||||
const std::vector<chat::ConversationMeta> previous_convs = convs_;
|
||||
convs_ = convs;
|
||||
if (team_btn_)
|
||||
{
|
||||
@@ -354,7 +372,11 @@ void ChatMessageListScreen::setConversations(const std::vector<chat::Conversatio
|
||||
filter_mode_ = FilterMode::Broadcast;
|
||||
}
|
||||
updateFilterHighlight();
|
||||
rebuildList();
|
||||
if (!conversation_identity_list_equal(previous_convs, convs_) ||
|
||||
!updateListInPlace(convs_))
|
||||
{
|
||||
rebuildList();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatMessageListScreen::setSelected(int index)
|
||||
@@ -617,6 +639,95 @@ void ChatMessageListScreen::rebuildList()
|
||||
chat::ui::message_list::input::on_ui_refreshed(&input_controller_);
|
||||
}
|
||||
|
||||
bool ChatMessageListScreen::updateListInPlace(const std::vector<chat::ConversationMeta>& convs)
|
||||
{
|
||||
if (!guard_ || !guard_->alive || !list_panel_ || !lv_obj_is_valid(list_panel_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!conversation_identity_list_equal(convs_, convs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<chat::ConversationMeta> filtered;
|
||||
filtered.reserve(convs.size());
|
||||
for (const auto& conv : convs)
|
||||
{
|
||||
if (is_team_conversation(conv.id))
|
||||
{
|
||||
if (filter_mode_ == FilterMode::Team)
|
||||
{
|
||||
filtered.push_back(conv);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (filter_mode_ == FilterMode::Direct && conv.id.peer != 0)
|
||||
{
|
||||
filtered.push_back(conv);
|
||||
}
|
||||
else if (filter_mode_ == FilterMode::Broadcast && conv.id.peer == 0)
|
||||
{
|
||||
filtered.push_back(conv);
|
||||
}
|
||||
}
|
||||
|
||||
if (filtered.size() != items_.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < filtered.size(); ++index)
|
||||
{
|
||||
if (!(items_[index].conv == filtered[index].id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < filtered.size(); ++index)
|
||||
{
|
||||
updateListItem(index, filtered[index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChatMessageListScreen::updateListItem(const size_t index,
|
||||
const chat::ConversationMeta& conv)
|
||||
{
|
||||
if (index >= items_.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MessageItem& item = items_[index];
|
||||
std::string title = "[" + std::string(protocol_short_label(conv.id.protocol)) + "] " + conv.name;
|
||||
lv_label_set_text(item.name_label, title.c_str());
|
||||
::ui::fonts::apply_chat_content_font(item.name_label, title.c_str());
|
||||
|
||||
std::string preview = truncate_preview(conv.preview);
|
||||
lv_label_set_text(item.preview_label, preview.c_str());
|
||||
::ui::fonts::apply_chat_content_font(item.preview_label, preview.c_str());
|
||||
|
||||
char time_buf[16];
|
||||
format_time_hhmm(time_buf, conv.last_timestamp);
|
||||
lv_label_set_text(item.time_label, time_buf);
|
||||
::ui::fonts::apply_ui_chrome_font(item.time_label);
|
||||
|
||||
if (conv.unread > 0)
|
||||
{
|
||||
char unread_str[16];
|
||||
snprintf(unread_str, sizeof(unread_str), "%d", conv.unread);
|
||||
lv_label_set_text(item.unread_label, unread_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv_label_set_text(item.unread_label, "");
|
||||
}
|
||||
::ui::fonts::apply_ui_chrome_font(item.unread_label);
|
||||
item.unread_count = conv.unread;
|
||||
}
|
||||
|
||||
void ChatMessageListScreen::item_event_cb(lv_event_t* e)
|
||||
{
|
||||
auto* screen =
|
||||
|
||||
@@ -333,12 +333,10 @@ void UiController::update()
|
||||
{
|
||||
// Process incoming messages
|
||||
service_.processIncoming();
|
||||
service_.flushStore();
|
||||
|
||||
// Refresh UI if needed
|
||||
if (state_ == State::ChannelList && channel_list_)
|
||||
{
|
||||
refreshUnreadCounts();
|
||||
}
|
||||
// Refresh UI only when an event marks the conversation list dirty.
|
||||
refreshUnreadCounts(false);
|
||||
}
|
||||
|
||||
void UiController::onChannelClicked(chat::ConversationId conv)
|
||||
@@ -416,19 +414,25 @@ void UiController::onChatEvent(sys::Event* event)
|
||||
// Note: Haptic feedback is now handled by the app runtime event pump
|
||||
// No need to call vibrator() here
|
||||
|
||||
if (state_ == State::Conversation &&
|
||||
(uint8_t)current_channel_ == msg_event->channel)
|
||||
const ChatMessage* latest = service_.getMessage(msg_event->msg_id);
|
||||
if (latest)
|
||||
{
|
||||
CHAT_UI_LOG("[UiController::onChatEvent] Updating conversation UI...\n");
|
||||
auto messages = service_.getRecentMessages(current_conv_, 50);
|
||||
conversation_->clearMessages();
|
||||
for (const auto& m : messages)
|
||||
const bool is_current_conversation =
|
||||
(state_ == State::Conversation) && (current_conv_ == chat::ConversationId(latest->channel,
|
||||
latest->peer,
|
||||
latest->protocol));
|
||||
updateConversationMetaForMessage(*latest, !is_current_conversation);
|
||||
if (is_current_conversation)
|
||||
{
|
||||
conversation_->addMessage(m);
|
||||
(void)updateConversationViewForIncoming(*latest);
|
||||
service_.markConversationRead(current_conv_);
|
||||
}
|
||||
else
|
||||
{
|
||||
conversation_list_dirty_ = true;
|
||||
}
|
||||
conversation_->scrollToBottom();
|
||||
}
|
||||
refreshUnreadCounts();
|
||||
refreshUnreadCounts(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -437,13 +441,17 @@ void UiController::onChatEvent(sys::Event* event)
|
||||
sys::ChatSendResultEvent* result_event = (sys::ChatSendResultEvent*)event;
|
||||
if (state_ == State::Conversation && conversation_)
|
||||
{
|
||||
auto messages = service_.getRecentMessages(current_conv_, 50);
|
||||
conversation_->clearMessages();
|
||||
for (const auto& m : messages)
|
||||
const ChatMessage* msg = service_.getMessage(result_event->msg_id);
|
||||
if (!msg || !conversation_->updateMessageStatus(result_event->msg_id, msg->status))
|
||||
{
|
||||
conversation_->addMessage(m);
|
||||
auto messages = service_.getRecentMessages(current_conv_, 50);
|
||||
conversation_->clearMessages();
|
||||
for (const auto& m : messages)
|
||||
{
|
||||
conversation_->addMessage(m);
|
||||
}
|
||||
conversation_->scrollToBottom();
|
||||
}
|
||||
conversation_->scrollToBottom();
|
||||
}
|
||||
(void)result_event;
|
||||
break;
|
||||
@@ -451,7 +459,8 @@ void UiController::onChatEvent(sys::Event* event)
|
||||
|
||||
case sys::EventType::ChatUnreadChanged:
|
||||
{
|
||||
refreshUnreadCounts();
|
||||
conversation_list_dirty_ = true;
|
||||
refreshUnreadCounts(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -519,7 +528,7 @@ void UiController::switchToChannelList()
|
||||
}
|
||||
|
||||
service_.setModelEnabled(true);
|
||||
refreshUnreadCounts();
|
||||
refreshUnreadCounts(true);
|
||||
}
|
||||
|
||||
void UiController::switchToConversation(chat::ConversationId conv)
|
||||
@@ -767,28 +776,29 @@ void UiController::handleSendMessage(const std::string& text)
|
||||
}
|
||||
|
||||
void UiController::refreshUnreadCounts()
|
||||
{
|
||||
refreshUnreadCounts(true);
|
||||
}
|
||||
|
||||
void UiController::refreshUnreadCounts(const bool force_reload)
|
||||
{
|
||||
if (!channel_list_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
auto convs = service_.getConversations(0, 0, &total);
|
||||
|
||||
// Update conversation names with contact nicknames
|
||||
for (auto& conv : convs)
|
||||
if (force_reload || conversation_list_dirty_ || cached_conversations_.empty())
|
||||
{
|
||||
if (conv.id.peer != 0)
|
||||
{
|
||||
std::string contact_name = app::messagingFacade().getContactService().getContactName(conv.id.peer);
|
||||
if (!contact_name.empty())
|
||||
{
|
||||
conv.name = contact_name;
|
||||
}
|
||||
// Otherwise keep the short_name from ConversationMeta
|
||||
}
|
||||
syncConversationListFromStore();
|
||||
}
|
||||
applyConversationListToUi();
|
||||
}
|
||||
|
||||
void UiController::syncConversationListFromStore()
|
||||
{
|
||||
size_t total = 0;
|
||||
cached_conversations_ = service_.getConversations(0, 0, &total);
|
||||
normalizeConversationNames(cached_conversations_);
|
||||
|
||||
team::ui::TeamUiSnapshot team_snap;
|
||||
if (team::ui::team_ui_get_store().load(team_snap) && team_snap.has_team_id)
|
||||
@@ -811,16 +821,97 @@ void UiController::refreshUnreadCounts()
|
||||
{
|
||||
team_conv.preview = "No messages";
|
||||
}
|
||||
convs.insert(convs.begin(), team_conv);
|
||||
cached_conversations_.insert(cached_conversations_.begin(), team_conv);
|
||||
}
|
||||
|
||||
channel_list_->setConversations(convs);
|
||||
channel_list_->setSelectedConversation(current_conv_);
|
||||
conversation_list_dirty_ = false;
|
||||
}
|
||||
|
||||
// Update header status (battery only, with icon)
|
||||
void UiController::normalizeConversationNames(std::vector<chat::ConversationMeta>& convs) const
|
||||
{
|
||||
for (auto& conv : convs)
|
||||
{
|
||||
if (conv.id.peer == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::string contact_name = app::messagingFacade().getContactService().getContactName(conv.id.peer);
|
||||
if (!contact_name.empty())
|
||||
{
|
||||
conv.name = contact_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UiController::applyConversationListToUi()
|
||||
{
|
||||
if (!channel_list_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
channel_list_->setConversations(cached_conversations_);
|
||||
channel_list_->setSelectedConversation(current_conv_);
|
||||
channel_list_->updateBatteryFromBoard();
|
||||
}
|
||||
|
||||
void UiController::updateConversationMetaForMessage(const chat::ChatMessage& msg,
|
||||
const bool increment_unread)
|
||||
{
|
||||
if (isTeamConversation(chat::ConversationId(msg.channel, msg.peer, msg.protocol)))
|
||||
{
|
||||
conversation_list_dirty_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
chat::ConversationMeta meta;
|
||||
meta.id = chat::ConversationId(msg.channel, msg.peer, msg.protocol);
|
||||
meta.name = (msg.peer == 0) ? "Broadcast" : resolve_contact_name(msg.peer);
|
||||
meta.preview = msg.text;
|
||||
meta.last_timestamp = msg.timestamp;
|
||||
meta.unread = (increment_unread && msg.status == chat::MessageStatus::Incoming) ? 1 : 0;
|
||||
|
||||
bool found = false;
|
||||
for (auto it = cached_conversations_.begin(); it != cached_conversations_.end(); ++it)
|
||||
{
|
||||
if (!(it->id == meta.id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
meta.unread += it->unread;
|
||||
if (!increment_unread && msg.status == chat::MessageStatus::Incoming)
|
||||
{
|
||||
meta.unread = 0;
|
||||
}
|
||||
cached_conversations_.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found && msg.peer == 0)
|
||||
{
|
||||
meta.name = "Broadcast";
|
||||
}
|
||||
|
||||
cached_conversations_.insert(cached_conversations_.begin(), meta);
|
||||
}
|
||||
|
||||
bool UiController::updateConversationViewForIncoming(const chat::ChatMessage& msg)
|
||||
{
|
||||
if (!conversation_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(current_conv_ == chat::ConversationId(msg.channel, msg.peer, msg.protocol)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
conversation_->addMessage(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UiController::isTeamConversation(const chat::ConversationId& conv) const
|
||||
{
|
||||
return isTeamConversationId(conv);
|
||||
|
||||
@@ -54,6 +54,7 @@ void updateCoreServices(app::IAppFacade& app_context)
|
||||
hostlink::process_pending_commands();
|
||||
|
||||
app_context.getChatService().processIncoming();
|
||||
app_context.getChatService().flushStore();
|
||||
|
||||
team::TeamService* team_service = app_context.getTeamService();
|
||||
if (team_service)
|
||||
|
||||
+10
@@ -26,6 +26,7 @@ class InternalFsStore final : public ::chat::IChatStore
|
||||
void clearConversation(const ::chat::ConversationId& conv) override;
|
||||
void clearAll() override;
|
||||
bool updateMessageStatus(::chat::MessageId msg_id, ::chat::MessageStatus status) override;
|
||||
void flush() override;
|
||||
|
||||
private:
|
||||
struct StoredMessageEntry
|
||||
@@ -107,6 +108,8 @@ class InternalFsStore final : public ::chat::IChatStore
|
||||
bool ensureFs() const;
|
||||
bool loadFromFs();
|
||||
bool saveToFs() const;
|
||||
void markDirty();
|
||||
void maybeSave(bool force = false);
|
||||
void evictOldestMessage();
|
||||
ConversationStorage& getConversationStorage(const ::chat::ConversationId& conv);
|
||||
const ConversationStorage& getConversationStorage(const ::chat::ConversationId& conv) const;
|
||||
@@ -115,6 +118,13 @@ class InternalFsStore final : public ::chat::IChatStore
|
||||
std::map<::chat::ConversationId, ConversationStorage> conversations_;
|
||||
uint32_t next_sequence_ = 1;
|
||||
size_t total_message_count_ = 0;
|
||||
mutable uint32_t last_save_ms_ = 0;
|
||||
mutable uint32_t dirty_since_ms_ = 0;
|
||||
mutable size_t pending_write_count_ = 0;
|
||||
mutable bool dirty_ = false;
|
||||
|
||||
static constexpr uint32_t kSaveIntervalMs = 1500;
|
||||
static constexpr size_t kMaxPendingWrites = 4;
|
||||
};
|
||||
|
||||
} // namespace platform::nrf52::arduino_common::chat::infra::store
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "sys/clock.h"
|
||||
|
||||
namespace platform::nrf52::arduino_common::chat::infra::store
|
||||
{
|
||||
@@ -39,7 +40,8 @@ void InternalFsStore::append(const ::chat::ChatMessage& msg)
|
||||
{
|
||||
storage.unread_count++;
|
||||
}
|
||||
(void)saveToFs();
|
||||
markDirty();
|
||||
maybeSave();
|
||||
}
|
||||
|
||||
std::vector<::chat::ChatMessage> InternalFsStore::loadRecent(const ::chat::ConversationId& conv, size_t n)
|
||||
@@ -119,7 +121,8 @@ std::vector<::chat::ConversationMeta> InternalFsStore::loadConversationPage(size
|
||||
void InternalFsStore::setUnread(const ::chat::ConversationId& conv, int unread)
|
||||
{
|
||||
getConversationStorage(conv).unread_count = unread;
|
||||
(void)saveToFs();
|
||||
markDirty();
|
||||
maybeSave();
|
||||
}
|
||||
|
||||
int InternalFsStore::getUnread(const ::chat::ConversationId& conv) const
|
||||
@@ -139,7 +142,8 @@ void InternalFsStore::clearConversation(const ::chat::ConversationId& conv)
|
||||
it->second.messages.clear();
|
||||
it->second.unread_count = 0;
|
||||
conversations_.erase(it);
|
||||
(void)saveToFs();
|
||||
markDirty();
|
||||
maybeSave(true);
|
||||
}
|
||||
|
||||
void InternalFsStore::clearAll()
|
||||
@@ -147,6 +151,9 @@ void InternalFsStore::clearAll()
|
||||
conversations_.clear();
|
||||
total_message_count_ = 0;
|
||||
next_sequence_ = 1;
|
||||
dirty_ = false;
|
||||
pending_write_count_ = 0;
|
||||
dirty_since_ms_ = 0;
|
||||
if (ensureFs() && path_ && InternalFS.exists(path_))
|
||||
{
|
||||
InternalFS.remove(path_);
|
||||
@@ -172,7 +179,8 @@ bool InternalFsStore::updateMessageStatus(::chat::MessageId msg_id, ::chat::Mess
|
||||
continue;
|
||||
}
|
||||
msg->status = status;
|
||||
(void)saveToFs();
|
||||
markDirty();
|
||||
maybeSave();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -312,6 +320,10 @@ bool InternalFsStore::loadFromFs()
|
||||
{
|
||||
evictOldestMessage();
|
||||
}
|
||||
dirty_ = false;
|
||||
pending_write_count_ = 0;
|
||||
dirty_since_ms_ = 0;
|
||||
last_save_ms_ = sys::millis_now();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -403,7 +415,49 @@ bool InternalFsStore::saveToFs() const
|
||||
{
|
||||
InternalFS.remove(path_);
|
||||
}
|
||||
return InternalFS.rename(temp_path.c_str(), path_);
|
||||
const bool renamed = InternalFS.rename(temp_path.c_str(), path_);
|
||||
if (renamed)
|
||||
{
|
||||
dirty_ = false;
|
||||
pending_write_count_ = 0;
|
||||
dirty_since_ms_ = 0;
|
||||
last_save_ms_ = sys::millis_now();
|
||||
}
|
||||
return renamed;
|
||||
}
|
||||
|
||||
void InternalFsStore::flush()
|
||||
{
|
||||
maybeSave(true);
|
||||
}
|
||||
|
||||
void InternalFsStore::markDirty()
|
||||
{
|
||||
if (!dirty_)
|
||||
{
|
||||
dirty_ = true;
|
||||
dirty_since_ms_ = sys::millis_now();
|
||||
}
|
||||
++pending_write_count_;
|
||||
}
|
||||
|
||||
void InternalFsStore::maybeSave(bool force)
|
||||
{
|
||||
if (!dirty_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t now_ms = sys::millis_now();
|
||||
const bool interval_elapsed =
|
||||
(dirty_since_ms_ != 0) && ((now_ms - dirty_since_ms_) >= kSaveIntervalMs);
|
||||
const bool too_many_pending = pending_write_count_ >= kMaxPendingWrites;
|
||||
if (!force && !interval_elapsed && !too_many_pending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(void)saveToFs();
|
||||
}
|
||||
|
||||
void InternalFsStore::evictOldestMessage()
|
||||
|
||||
Reference in New Issue
Block a user