Refactor app modules and add clang-format

This commit is contained in:
2026-04-24 06:34:30 +03:00
parent 0de7073f00
commit 70c2e77957
12 changed files with 2354 additions and 2011 deletions

View File

@@ -2,15 +2,19 @@
#include <algorithm>
#include <curses.h>
#include "app_ui.h"
#include "util.h"
namespace telegram_tui {
namespace {
std::string join_with_separator_local(const std::vector<std::string>& parts, const char* separator) {
std::string join_with_separator_local(const std::vector<std::string> &parts,
const char *separator) {
std::string joined;
for (const auto& part : parts) {
for (const auto &part : parts) {
if (part.empty()) {
continue;
}
@@ -22,7 +26,7 @@ std::string join_with_separator_local(const std::vector<std::string>& parts, con
return joined;
}
std::string format_user_status(const json& status) {
std::string format_user_status(const json &status) {
const std::string type = safe_string(status, "@type");
if (type == "userStatusOnline") {
return "online";
@@ -43,11 +47,11 @@ std::string format_user_status(const json& status) {
return {};
}
std::string primary_username(const json& object) {
std::string primary_username(const json &object) {
const json usernames = object.value("usernames", json::object());
const json active_usernames = usernames.value("active_usernames", json::array());
if (active_usernames.is_array()) {
for (const auto& username : active_usernames) {
for (const auto &username : active_usernames) {
if (username.is_string()) {
const std::string value = username.get<std::string>();
if (!value.empty()) {
@@ -59,19 +63,19 @@ std::string primary_username(const json& object) {
return safe_string(object, "username");
}
} // namespace
} // namespace
void App::update_user_status(std::int64_t user_id, const json& status) {
UserInfo& info = users_[user_id];
void App::update_user_status(std::int64_t user_id, const json &status) {
UserInfo &info = users_[user_id];
info.id = user_id;
info.status = format_user_status(status);
}
void App::upsert_user(const json& user) {
void App::upsert_user(const json &user) {
if (user.is_null()) {
return;
}
UserInfo& info = users_[safe_i64(user, "id")];
UserInfo &info = users_[safe_i64(user, "id")];
info.id = safe_i64(user, "id");
info.first_name = safe_string(user, "first_name");
info.last_name = safe_string(user, "last_name");
@@ -79,13 +83,13 @@ void App::upsert_user(const json& user) {
info.status = format_user_status(user.value("status", json::object()));
}
void App::upsert_basic_group(const json& basic_group) {
void App::upsert_basic_group(const json &basic_group) {
if (basic_group.is_null()) {
return;
}
const std::int64_t basic_group_id = safe_i64(basic_group, "id");
const std::int32_t member_count = safe_i32(basic_group, "member_count");
for (auto& [chat_id, chat] : chats_) {
for (auto &[chat_id, chat] : chats_) {
if (chat.basic_group_id != basic_group_id) {
continue;
}
@@ -96,7 +100,7 @@ void App::upsert_basic_group(const json& basic_group) {
}
}
void App::upsert_supergroup(const json& supergroup) {
void App::upsert_supergroup(const json &supergroup) {
if (supergroup.is_null()) {
return;
}
@@ -104,7 +108,7 @@ void App::upsert_supergroup(const json& supergroup) {
const std::int32_t member_count = safe_i32(supergroup, "member_count");
const bool is_channel = supergroup.value("is_channel", false);
const std::string username = primary_username(supergroup);
for (auto& [chat_id, chat] : chats_) {
for (auto &[chat_id, chat] : chats_) {
if (chat.supergroup_id != supergroup_id) {
continue;
}
@@ -116,13 +120,13 @@ void App::upsert_supergroup(const json& supergroup) {
}
}
void App::upsert_chat(const json& chat_object) {
void App::upsert_chat(const json &chat_object) {
if (chat_object.is_null()) {
return;
}
const std::int64_t chat_id = safe_i64(chat_object, "id");
ChatInfo& chat = chats_[chat_id];
ChatInfo &chat = chats_[chat_id];
chat.id = chat_id;
if (chat_object.contains("title")) {
chat.title = safe_string(chat_object, "title");
@@ -156,8 +160,8 @@ void App::upsert_chat(const json& chat_object) {
chat.username.clear();
}
if (chat.private_user_id != previous_private_user_id ||
chat.basic_group_id != previous_basic_group_id ||
chat.supergroup_id != previous_supergroup_id) {
chat.basic_group_id != previous_basic_group_id ||
chat.supergroup_id != previous_supergroup_id) {
chat.details_requested = false;
}
chat.unread_count = safe_i32(chat_object, "unread_count");
@@ -169,7 +173,7 @@ void App::upsert_chat(const json& chat_object) {
if (chat_object.contains("positions") && chat_object.at("positions").is_array()) {
chat.in_main_list = false;
chat.main_order = 0;
for (const auto& position : chat_object.at("positions")) {
for (const auto &position : chat_object.at("positions")) {
apply_chat_position(chat, position);
}
}
@@ -179,7 +183,7 @@ void App::upsert_chat(const json& chat_object) {
resort_chats();
}
void App::apply_chat_position(ChatInfo& chat, const json& position) {
void App::apply_chat_position(ChatInfo &chat, const json &position) {
if (!position.is_object()) {
return;
}
@@ -194,43 +198,58 @@ void App::apply_chat_position(ChatInfo& chat, const json& position) {
void App::resort_chats() {
if (sorted_chat_ids_.empty()) {
for (const auto& [chat_id, chat] : chats_) {
for (const auto &[chat_id, chat] : chats_) {
if (chat.in_main_list) {
sorted_chat_ids_.push_back(chat_id);
}
}
} else {
for (const auto& [chat_id, chat] : chats_) {
for (const auto &[chat_id, chat] : chats_) {
if (!chat.in_main_list) {
continue;
}
if (std::find(sorted_chat_ids_.begin(), sorted_chat_ids_.end(), chat_id) == sorted_chat_ids_.end()) {
if (std::find(sorted_chat_ids_.begin(), sorted_chat_ids_.end(), chat_id) ==
sorted_chat_ids_.end()) {
sorted_chat_ids_.push_back(chat_id);
}
}
}
std::stable_sort(sorted_chat_ids_.begin(), sorted_chat_ids_.end(),
[&](std::int64_t lhs, std::int64_t rhs) {
const auto left_it = chats_.find(lhs);
const auto right_it = chats_.find(rhs);
const bool left_has_order = left_it != chats_.end() && left_it->second.in_main_list && left_it->second.main_order > 0;
const bool right_has_order = right_it != chats_.end() && right_it->second.in_main_list && right_it->second.main_order > 0;
if (left_has_order != right_has_order) {
return left_has_order;
}
if (left_has_order && right_has_order && left_it->second.main_order != right_it->second.main_order) {
return left_it->second.main_order > right_it->second.main_order;
}
return false;
});
[&](std::int64_t lhs, std::int64_t rhs) {
const auto left_it = chats_.find(lhs);
const auto right_it = chats_.find(rhs);
const bool left_has_order = left_it != chats_.end() &&
left_it->second.in_main_list &&
left_it->second.main_order > 0;
const bool right_has_order = right_it != chats_.end() &&
right_it->second.in_main_list &&
right_it->second.main_order > 0;
if (left_has_order != right_has_order) {
return left_has_order;
}
if (left_has_order && right_has_order &&
left_it->second.main_order != right_it->second.main_order) {
return left_it->second.main_order >
right_it->second.main_order;
}
return false;
});
if (selected_chat_index_ >= static_cast<int>(sorted_chat_ids_.size())) {
selected_chat_index_ = std::max(0, static_cast<int>(sorted_chat_ids_.size()) - 1);
}
}
std::string App::format_open_chat_header(const ChatInfo& chat) const {
std::string App::user_status_label(std::int64_t user_id) const {
const auto user_it = users_.find(user_id);
if (user_it == users_.end()) {
return {};
}
return user_it->second.status;
}
std::string App::format_open_chat_header(const ChatInfo &chat) const {
std::vector<std::string> parts;
if (!chat.title.empty()) {
parts.push_back(chat.title);
@@ -256,8 +275,8 @@ std::string App::format_open_chat_header(const ChatInfo& chat) const {
parts.push_back("@" + chat.username);
}
if (chat.has_member_count) {
parts.push_back(
std::to_string(chat.member_count) + " " + (chat.is_channel ? "subscribers" : "members"));
parts.push_back(std::to_string(chat.member_count) + " " +
(chat.is_channel ? "subscribers" : "members"));
}
if (!chat.is_channel && chat.has_online_member_count) {
parts.push_back(std::to_string(chat.online_member_count) + " online");
@@ -266,4 +285,60 @@ std::string App::format_open_chat_header(const ChatInfo& chat) const {
return join_with_separator_local(parts, " | ");
}
} // namespace telegram_tui
void App::draw_chat_pane(int top, int height, int width) {
const int visible_rows = std::max(1, height);
int first_index = 0;
if (selected_chat_index_ >= visible_rows) {
first_index = selected_chat_index_ - visible_rows + 1;
}
for (int row = 0; row < visible_rows; ++row) {
const int index = first_index + row;
const int y = top + row;
mvhline(y, 0, ' ', width);
if (index >= static_cast<int>(sorted_chat_ids_.size())) {
continue;
}
const auto chat_id = sorted_chat_ids_[index];
const auto chat_it = chats_.find(chat_id);
const bool selected = index == selected_chat_index_;
const bool is_open = open_chat_id_ == chat_id;
if (selected) {
attron(A_BOLD | (focus_ == FocusPane::Chats ? A_REVERSE : A_NORMAL));
}
if (is_open) {
attron(COLOR_PAIR(kColorPairOpenChat));
}
const bool has_chat = chat_it != chats_.end();
const ChatInfo *chat = has_chat ? &chat_it->second : nullptr;
std::string line = (chat != nullptr && chat->unread_count > 0) ? "[U] " : "[ ] ";
line += is_open ? ">" : " ";
if (chat != nullptr) {
line += chat->title;
} else {
line += "Chat " + std::to_string(chat_id);
}
if (chat != nullptr && chat->unread_count > 0) {
line += " [" + std::to_string(chat->unread_count) + "]";
}
if (chat != nullptr && !chat->last_message_preview.empty()) {
line += " - " + chat->last_message_preview;
}
if (static_cast<int>(line.size()) > width - 2) {
line.resize(static_cast<std::size_t>(width - 5));
line += "...";
}
mvprintw(y, 1, "%s", line.c_str());
if (is_open) {
attroff(COLOR_PAIR(kColorPairOpenChat));
}
if (selected) {
attroff(A_BOLD | (focus_ == FocusPane::Chats ? A_REVERSE : A_NORMAL));
}
}
}
} // namespace telegram_tui