Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions src/telephony/client/ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,15 +828,61 @@ 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()
toolbar.set_content(page)

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)]
Expand Down Expand Up @@ -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))
Expand All @@ -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."""
Expand Down
39 changes: 27 additions & 12 deletions src/telephony/client/ui/windows/blocklist_editor_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down