mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-11 04:57:20 +00:00
perf: reduce chat list and message handling latency
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user