From 20b4cdc5f145af19660da88bf0a0a24409f1ddea Mon Sep 17 00:00:00 2001 From: Green Sky Date: Fri, 12 Jan 2024 16:45:52 +0100 Subject: [PATCH] sync and delivery (and unused read) states and other smaller refactors --- external/solanaceae_message3 | 2 +- external/solanaceae_tox | 2 +- external/solanaceae_toxcore | 2 +- src/chat_gui4.cpp | 125 ++++++++++++++++++++++++++++++++--- src/message_image_loader.cpp | 7 +- src/send_image_popup.cpp | 9 ++- src/texture_cache.cpp | 6 +- src/texture_cache.hpp | 8 ++- src/tox_avatar_loader.cpp | 11 ++- 9 files changed, 147 insertions(+), 25 deletions(-) diff --git a/external/solanaceae_message3 b/external/solanaceae_message3 index d0a53a63..4ae7c406 160000 --- a/external/solanaceae_message3 +++ b/external/solanaceae_message3 @@ -1 +1 @@ -Subproject commit d0a53a6366e3f52e8c84d9fd63dd925ef93e10ce +Subproject commit 4ae7c4068f91cedd4291521a5777af00c0ff052f diff --git a/external/solanaceae_tox b/external/solanaceae_tox index c01d9114..75ed1dff 160000 --- a/external/solanaceae_tox +++ b/external/solanaceae_tox @@ -1 +1 @@ -Subproject commit c01d91144ce10486ff6e98a2e6e8cc5e20a5c412 +Subproject commit 75ed1dffebe254d9cc79753d31f4c73c40238f72 diff --git a/external/solanaceae_toxcore b/external/solanaceae_toxcore index 80140ae9..9d3d81d4 160000 --- a/external/solanaceae_toxcore +++ b/external/solanaceae_toxcore @@ -1 +1 @@ -Subproject commit 80140ae9b6cc9d6280cfc8c16c6f8c3e40b51f2a +Subproject commit 9d3d81d4ff55b152cb779cec2a37b960fcedd584 diff --git a/src/chat_gui4.cpp b/src/chat_gui4.cpp index 730dd643..e790a7f4 100644 --- a/src/chat_gui4.cpp +++ b/src/chat_gui4.cpp @@ -123,14 +123,20 @@ void ChatGui4::render(float time_delta) { if (_selected_contact) { const std::string chat_label = "chat " + std::to_string(entt::to_integral(*_selected_contact)); + + const std::vector* sub_contacts = nullptr; + if (_cr.all_of(*_selected_contact)) { + sub_contacts = &_cr.get(*_selected_contact).subs; + } + if (ImGui::BeginChild(chat_label.c_str(), {0, 0}, true)) { - if (_cr.all_of(*_selected_contact)) { - const auto sub_contacts = _cr.get(*_selected_contact).subs; - if (!sub_contacts.empty()) { + //if (_cr.all_of(*_selected_contact)) { + if (sub_contacts != nullptr) { + if (!sub_contacts->empty()) { if (ImGui::BeginChild("subcontacts", {150, -100}, true)) { - ImGui::Text("subs: %zu", sub_contacts.size()); + ImGui::Text("subs: %zu", sub_contacts->size()); ImGui::Separator(); - for (const auto& c : sub_contacts) { + for (const auto& c : *sub_contacts) { // TODO: can a sub be selected? no if (renderSubContactListContact(c, _selected_contact.has_value() && *_selected_contact == c)) { _text_input_buffer.insert(0, (_cr.all_of(c) ? _cr.get(c).name : "") + ": "); @@ -233,9 +239,10 @@ void ChatGui4::render(float time_delta) { ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit ; - if (msg_reg_ptr != nullptr && ImGui::BeginTable("chat_table", 4, table_flags)) { + if (msg_reg_ptr != nullptr && ImGui::BeginTable("chat_table", 5, table_flags)) { ImGui::TableSetupColumn("name", 0, TEXT_BASE_WIDTH * 15.f); ImGui::TableSetupColumn("message", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("delivered/read"); ImGui::TableSetupColumn("timestamp"); ImGui::TableSetupColumn("extra_info", _show_chat_extra_info ? ImGuiTableColumnFlags_None : ImGuiTableColumnFlags_Disabled); @@ -354,6 +361,84 @@ void ChatGui4::render(float time_delta) { ImGui::TextDisabled("---"); } + // remote received and read state + if (ImGui::TableNextColumn()) { + // TODO: theming for hardcoded values + + if (msg_reg.all_of(e)) { + const auto list = msg_reg.get(e).ts; + // wrongly assumes contacts never get removed from a group + if (sub_contacts != nullptr && list.size() < sub_contacts->size()) { + // if partically delivered + ImGui::TextColored(ImVec4{0.7f, 0.7f, 0.1f, 0.5f}, "d"); + } else { + // if fully delivered + ImGui::TextColored(ImVec4{0.1f, 0.8f, 0.1f, 0.5f}, "D"); + } + + if (ImGui::BeginItemTooltip()) { + std::string synced_by_text {"delivery confirmed by:"}; + const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); + + for (const auto& [c, syned_ts] : list) { + if (_cr.all_of(c)) { + synced_by_text += "\n sself(!)"; // makes no sense + } else if (_cr.all_of(c)) { + synced_by_text += "\n wself"; + } else { + synced_by_text += "\n >" + (_cr.all_of(c) ? _cr.get(c).name : ""); + } + const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1; + synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)"; + } + + ImGui::Text("%s", synced_by_text.c_str()); + + ImGui::EndTooltip(); + } + } else { + ImGui::TextDisabled("_"); + } + + ImGui::SameLine(); + + // TODO: dedup + if (msg_reg.all_of(e)) { + const auto list = msg_reg.get(e).ts; + // wrongly assumes contacts never get removed from a group + if (sub_contacts != nullptr && list.size() < sub_contacts->size()) { + // if partially read + ImGui::TextColored(ImVec4{0.7f, 0.7f, 0.1f, 0.5f}, "r"); + } else { + // if fully read + ImGui::TextColored(ImVec4{0.1f, 0.8f, 0.1f, 0.5f}, "R"); + } + + if (ImGui::BeginItemTooltip()) { + std::string synced_by_text {"read confirmed by:"}; + const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); + + for (const auto& [c, syned_ts] : list) { + if (_cr.all_of(c)) { + synced_by_text += "\n sself(!)"; // makes no sense + } else if (_cr.all_of(c)) { + synced_by_text += "\n wself"; + } else { + synced_by_text += "\n >" + (_cr.all_of(c) ? _cr.get(c).name : ""); + } + const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1; + synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)"; + } + + ImGui::Text("%s", synced_by_text.c_str()); + + ImGui::EndTooltip(); + } + } else { + ImGui::TextDisabled("_"); + } + } + // ts if (ImGui::TableNextColumn()) { auto time = std::chrono::system_clock::to_time_t( @@ -707,8 +792,9 @@ void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) { if (reg.all_of(e)) { std::string synced_by_text {"syncedBy:"}; - const auto& synced_by = reg.get(e).list; - for (const auto& c : synced_by) { + const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); + + for (const auto& [c, syned_ts] : reg.get(e).ts) { if (_cr.all_of(c)) { synced_by_text += "\n sself"; } else if (_cr.all_of(c)) { @@ -716,7 +802,30 @@ void ChatGui4::renderMessageExtra(Message3Registry& reg, const Message3 e) { } else { synced_by_text += "\n >" + (_cr.all_of(c) ? _cr.get(c).name : ""); } + const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1; + synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)"; } + + ImGui::TextDisabled("%s", synced_by_text.c_str()); + } + + // TODO: remove? + if (reg.all_of(e)) { + std::string synced_by_text {"receivedBy:"}; + const int64_t now_ts_s = int64_t(Message::getTimeMS() / 1000u); + + for (const auto& [c, syned_ts] : reg.get(e).ts) { + if (_cr.all_of(c)) { + synced_by_text += "\n sself(!)"; // makes no sense + } else if (_cr.all_of(c)) { + synced_by_text += "\n wself"; + } else { + synced_by_text += "\n >" + (_cr.all_of(c) ? _cr.get(c).name : ""); + } + const int64_t seconds_ago = (int64_t(syned_ts / 1000u) - now_ts_s) * -1; + synced_by_text += " (" + std::to_string(seconds_ago) + "sec ago)"; + } + ImGui::TextDisabled("%s", synced_by_text.c_str()); } } diff --git a/src/message_image_loader.cpp b/src/message_image_loader.cpp index 10687905..ecd3281b 100644 --- a/src/message_image_loader.cpp +++ b/src/message_image_loader.cpp @@ -12,6 +12,11 @@ #include #include +// fwd +namespace Message { +uint64_t getTimeMS(void); +} + MessageImageLoader::MessageImageLoader(void) { _image_loaders.push_back(std::make_unique()); _image_loaders.push_back(std::make_unique()); @@ -52,7 +57,7 @@ std::optional MessageImageLoader::load(TextureUploaderI& tu, Messa } TextureEntry new_entry; - new_entry.timestamp_last_rendered = getNowMS(); + new_entry.timestamp_last_rendered = Message::getTimeMS(); new_entry.current_texture = 0; for (const auto& [ms, data] : res.frames) { const auto n_t = tu.uploadRGBA(data.data(), res.width, res.height); diff --git a/src/send_image_popup.cpp b/src/send_image_popup.cpp index 00ff6864..4c046afa 100644 --- a/src/send_image_popup.cpp +++ b/src/send_image_popup.cpp @@ -6,6 +6,11 @@ #include +// fwd +namespace Message { +uint64_t getTimeMS(void); +} + SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) { _image_loaders.push_back(std::make_unique()); _image_loaders.push_back(std::make_unique()); @@ -76,7 +81,7 @@ bool SendImagePopup::load(void) { } } - preview_image.timestamp_last_rendered = getNowMS(); + preview_image.timestamp_last_rendered = Message::getTimeMS(); preview_image.current_texture = 0; for (const auto& [ms, data] : original_image.frames) { const auto n_t = _tu.uploadRGBA(data.data(), original_image.width, original_image.height); @@ -169,7 +174,7 @@ void SendImagePopup::render(float time_delta) { const auto TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const auto TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); - preview_image.doAnimation(getNowMS()); + preview_image.doAnimation(Message::getTimeMS()); time += time_delta; time = fmod(time, 1.f); // fract() diff --git a/src/texture_cache.cpp b/src/texture_cache.cpp index 32c4d4c8..382af96e 100644 --- a/src/texture_cache.cpp +++ b/src/texture_cache.cpp @@ -21,7 +21,7 @@ void TextureEntry::doAnimation(const int64_t ts_now) { TextureEntry generateTestAnim(TextureUploaderI& tu) { TextureEntry new_entry; - new_entry.timestamp_last_rendered = getNowMS(); + new_entry.timestamp_last_rendered = Message::getTimeMS(); new_entry.current_texture = 0; for (size_t i = 0; i < 4; i++) { // hack @@ -54,7 +54,3 @@ TextureEntry generateTestAnim(TextureUploaderI& tu) { return new_entry; } -uint64_t getNowMS(void) { - return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); -} - diff --git a/src/texture_cache.hpp b/src/texture_cache.hpp index 5f09deb7..41208208 100644 --- a/src/texture_cache.hpp +++ b/src/texture_cache.hpp @@ -71,8 +71,10 @@ struct TextureEntry { TextureEntry generateTestAnim(TextureUploaderI& tu); -// TODO: move to utils or something -uint64_t getNowMS(void); +// fwd +namespace Message { +uint64_t getTimeMS(void); +} template struct TextureCache { @@ -132,7 +134,7 @@ struct TextureCache { } void update(void) { - const uint64_t ts_now = getNowMS(); + const uint64_t ts_now = Message::getTimeMS(); std::vector to_purge; for (auto&& [key, te] : _cache) { diff --git a/src/tox_avatar_loader.cpp b/src/tox_avatar_loader.cpp index 54058d3b..1e523222 100644 --- a/src/tox_avatar_loader.cpp +++ b/src/tox_avatar_loader.cpp @@ -14,6 +14,11 @@ #include #include +// fwd +namespace Message { +uint64_t getTimeMS(void); +} + ToxAvatarLoader::ToxAvatarLoader(Contact3Registry& cr) : _cr(cr) { _image_loaders.push_back(std::make_unique()); _image_loaders.push_back(std::make_unique()); @@ -121,7 +126,7 @@ std::optional ToxAvatarLoader::load(TextureUploaderI& tu, Contact3 const auto& a_m = _cr.get(c); TextureEntry new_entry; - new_entry.timestamp_last_rendered = getNowMS(); + new_entry.timestamp_last_rendered = Message::getTimeMS(); new_entry.current_texture = 0; new_entry.width = a_m.width; @@ -159,7 +164,7 @@ std::optional ToxAvatarLoader::load(TextureUploaderI& tu, Contact3 } TextureEntry new_entry; - new_entry.timestamp_last_rendered = getNowMS(); + new_entry.timestamp_last_rendered = Message::getTimeMS(); new_entry.current_texture = 0; for (const auto& [ms, data] : res.frames) { const auto n_t = tu.uploadRGBA(data.data(), res.width, res.height); @@ -195,7 +200,7 @@ std::optional ToxAvatarLoader::load(TextureUploaderI& tu, Contact3 } TextureEntry new_entry; - new_entry.timestamp_last_rendered = getNowMS(); + new_entry.timestamp_last_rendered = Message::getTimeMS(); new_entry.current_texture = 0; const auto n_t = tu.uploadRGBA(pixels.data(), 5, 5);