From dd7d53cbcc8bd129a5b252c80b17aa2a9cdb54a6 Mon Sep 17 00:00:00 2001 From: alaraajavamma Date: Sat, 22 Aug 2026 20:00:20 +0300 Subject: [PATCH] fix: let a flow return to a page that tells the truth two flows dropped the user back onto a page describing the world before the flow ran: the block-and-remove confirmation fell back into the block sheet as if the answer had not worked, and saving a contact from call details returned to the old name. the sheet now leaves with the answer, and call details rebuilds both on return and when the data actually arrives, since the mirrors learn about a save only after the flow has popped back. one quirk: a popped page still holds its parent while the hidden signal runs, so the alive-check waits one idle. --- src/telephony/client/ui/main_window.py | 57 +++++++++++++++++-- .../ui/windows/blocklist_editor_window.py | 39 +++++++++---- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/telephony/client/ui/main_window.py b/src/telephony/client/ui/main_window.py index 554a8860..7327e7a7 100644 --- a/src/telephony/client/ui/main_window.py +++ b/src/telephony/client/ui/main_window.py @@ -828,7 +828,52 @@ def done(success): run_in_background(self.ofono.dial, number, on_complete=done, hide_id=hide_id) def show_call_details(self, item): - """Show the call details sheet for a history item.""" + """Show the call details sheet for a history item. + + The page rebuilds itself whenever it comes back into view, + because the flows it opens change what it says: saving the + number as a contact changes the name and which actions apply, + and blocking it swaps the block action for an unblock. A page + built once would come back telling the state before the flow. + """ + page = Adw.NavigationPage(title=_("Call Details")) + + def rebuild(*_args): + page.set_child(self._build_call_details(item)) + return False + + connections = [(self.eds, self.eds.connect('contacts-loaded', + lambda *_a: GLib.idle_add(rebuild))), + (self.db, self.db.connect('blocklist-updated', + lambda *_a: GLib.idle_add(rebuild)))] + + def on_hidden(p): + """Release the change subscriptions once the page leaves the stack. + + Hidden also fires when another page merely covers this one, + where the subscriptions must survive: the covering flow is + exactly what changes the answers, and its pop is too early + for the mirrors, so the page listens for the data itself. + + A popped page still has its parent while the signal runs + and loses it a moment later, so the check waits one idle; + asking at signal time answers kept for both fates. + """ + def check(): + if p.get_parent() is not None: + return False + for obj, handler in connections: + obj.disconnect(handler) + connections.clear() + return False + GLib.idle_add(check) + + page.connect("showing", rebuild) + page.connect("hidden", on_hidden) + present_sheet_page(self, page) + + def _build_call_details(self, item): + """Build the call details content from the current state.""" toolbar = Adw.ToolbarView() toolbar.add_top_bar(Adw.HeaderBar()) page = Adw.PreferencesPage() @@ -836,7 +881,8 @@ def show_call_details(self, item): grp_info = Adw.PreferencesGroup() page.add(grp_info) - rows = [(_("Number"), item.number), (_("Name"), item.name), + current_name = self.eds.get_contact_name(item.number) or item.name + rows = [(_("Number"), item.number), (_("Name"), current_name), (_("Direction"), call_direction_text(item.direction)), (_("Result"), call_outcome_text(item.direction, item.disconnect_reason)), (_("Date"), item.full_ts), (_("Duration"), item.duration_str)] @@ -892,11 +938,12 @@ def _unblock(): lambda: self.notify_success(_("Unblocked"))) add_action(_("Unblock Number"), _unblock, needs_eds=True, opens_flow=True) else: - lbl = _("Edit Contact") if item.is_saved else _("Add to Contacts") + is_saved = self.eds.get_contact_name(item.number) is not None + lbl = _("Edit Contact") if is_saved else _("Add to Contacts") add_action(lbl, lambda: self.present_edit_contact(number_preset=item.number), needs_eds=True, opens_flow=True) - if not item.is_saved: + if not is_saved: add_action(_("Add to Existing Contact"), lambda: self.on_add_to_existing(item), needs_eds=True, opens_flow=True) add_action(_("Search Number"), lambda: self.search_number_online(item.number)) @@ -918,7 +965,7 @@ def _unblock(): self.daemon.delete_call_entry(item.id)]), destructive=True, opens_flow=True) - present_sheet_page(self, Adw.NavigationPage(title=_("Call Details"), child=toolbar)) + return toolbar def search_number_online(self, number): """Open the configured search engine for a phone number.""" diff --git a/src/telephony/client/ui/windows/blocklist_editor_window.py b/src/telephony/client/ui/windows/blocklist_editor_window.py index 8d8116cb..aa064f79 100644 --- a/src/telephony/client/ui/windows/blocklist_editor_window.py +++ b/src/telephony/client/ui/windows/blocklist_editor_window.py @@ -95,24 +95,39 @@ def on_save(self, btn): self._show_error(_("Duplicate"), _("This number is already blocked.")) return - def _do_block(): - def done(success): - if success: - logger.info(f"[Blocklist] Added number: {norm_num}") - close_sheet_page(self.get_root()) - else: - self._show_error(_("Database Error"), _("Failed to save to blocklist.")) - - block_calls = self.sw_calls.get_active() - block_messages = self.sw_messages.get_active() + block_calls = bool(self.sw_calls and self.sw_calls.get_active()) + block_messages = bool(self.sw_messages and self.sw_messages.get_active()) + + block_calls = self.sw_calls.get_active() + block_messages = self.sw_messages.get_active() + + def _start_block(done): run_in_background(self.daemon.add_blocked_number, norm_num, note, block_calls, block_messages, on_complete=done) + def _confirmed(): + """Leave first, then block. + + Answering the question is the end of the flow. Falling back + into the block sheet for however long the write takes reads + as the question not having worked, so the sheet goes first + and the write reports through a toast if it fails. + """ + close_sheet_page(self.get_root()) + _start_block(lambda ok: None if ok else + self.app_window.notify_error(_("Failed to save to blocklist."))) + if self.eds.search_contacts(norm_num): - self._confirm_block_remove(raw_num, _do_block) + self._confirm_block_remove(raw_num, _confirmed) return - _do_block() + def _direct_done(success): + if success: + close_sheet_page(self.get_root()) + else: + self._show_error(_("Database Error"), _("Failed to save to blocklist.")) + + _start_block(_direct_done) def _confirm_block_remove(self, _number_str, on_confirm): """Show confirmation to block and remove from contacts."""