feat(chat): hide conversations + contact address picker for new conversation
Hide conversations: - A "Hide" action in the thread header drops a conversation from the list. The messages stay in the seed-encrypted store (on-chain history can't be deleted); a new INCOMING message un-hides it (you can't un-receive), so nothing is lost. - Hidden cids persist in settings (mirrors the mute list) and are skipped by both the conversation list and the unread badge. New-conversation address picker: - A "Choose from contacts" dropdown lists the address book's shielded (z-address) contacts and fills the recipient field on selection; manual paste still works. 8-language strings + CJK subset (+1 glyph 届). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,7 @@ void RenderChatTab(App* app)
|
||||
// Build conversation summaries (single scan per conversation), sorted by most-recent activity.
|
||||
std::vector<ConvSummary> convs;
|
||||
for (const auto& cid : store.conversationIds()) {
|
||||
if (app->settings() && app->settings()->isChatHidden(cid)) continue; // hidden — a new message un-hides it
|
||||
const auto messages = store.conversation(cid);
|
||||
if (messages.empty()) continue;
|
||||
ConvSummary c;
|
||||
@@ -439,6 +440,16 @@ void RenderChatTab(App* app)
|
||||
app->settings()->setChatMuted(sel->cid, !muted);
|
||||
app->settings()->save();
|
||||
}
|
||||
// Hide conversation — drops it from the list (messages stay in the encrypted store; a
|
||||
// new incoming message brings it back). Deselect so the thread resets to the next one.
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(TR("chat_hide")) && app->settings()) {
|
||||
app->markChatConversationSeen(sel->cid, sel->lastTs); // don't leave a phantom unread
|
||||
app->settings()->setChatHidden(sel->cid, true);
|
||||
app->settings()->save();
|
||||
s_selected_cid.clear();
|
||||
Notifications::instance().info(TR("chat_hidden_toast"));
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -620,6 +631,27 @@ void RenderChatTab(App* app)
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
const float fieldW = ImGui::GetContentRegionAvail().x;
|
||||
material::LabeledInput(TR("chat_new_zaddr"), "##newz", s_new_zaddr, sizeof(s_new_zaddr), fieldW);
|
||||
// Or pick from contacts — chat needs a shielded z-address, so only z-addr contacts are listed.
|
||||
// Selecting one fills the field above (manual paste still works).
|
||||
ImGui::SetNextItemWidth(fieldW);
|
||||
if (ImGui::BeginCombo("##newzpick", TR("chat_pick_contact"), ImGuiComboFlags_HeightLarge)) {
|
||||
int shown = 0;
|
||||
for (const auto& e : book.entries()) {
|
||||
if (e.address.empty() || e.address[0] != 'z') continue; // chat requires a z-address
|
||||
++shown;
|
||||
const std::string item = e.label + " " + shorten(e.address, 16, 8);
|
||||
if (ImGui::Selectable(item.c_str())) {
|
||||
std::strncpy(s_new_zaddr, e.address.c_str(), sizeof(s_new_zaddr) - 1);
|
||||
s_new_zaddr[sizeof(s_new_zaddr) - 1] = '\0';
|
||||
}
|
||||
}
|
||||
if (shown == 0) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(TR("chat_no_z_contacts"));
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
material::LabeledInput(TR("chat_new_message"), "##newm", s_new_msg, sizeof(s_new_msg), fieldW);
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
|
||||
Reference in New Issue
Block a user