Merge pull request #661 from PR0M3TH3AN/codex/refactor-entry-source-and-update-sync-methods

Switch entry service to background sync
This commit is contained in:
thePR0M3TH3AN
2025-07-19 15:57:54 -04:00
committed by GitHub
7 changed files with 47 additions and 26 deletions

View File

@@ -282,7 +282,7 @@ class EntryService:
) -> int: ) -> int:
with self._lock: with self._lock:
idx = self._manager.entry_manager.add_entry(label, length, username, url) idx = self._manager.entry_manager.add_entry(label, length, username, url)
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_totp( def add_totp(
@@ -303,7 +303,7 @@ class EntryService:
period=period, period=period,
digits=digits, digits=digits,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return uri return uri
def add_ssh_key( def add_ssh_key(
@@ -320,7 +320,7 @@ class EntryService:
index=index, index=index,
notes=notes, notes=notes,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_pgp_key( def add_pgp_key(
@@ -341,7 +341,7 @@ class EntryService:
user_id=user_id, user_id=user_id,
notes=notes, notes=notes,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_nostr_key( def add_nostr_key(
@@ -357,7 +357,7 @@ class EntryService:
index=index, index=index,
notes=notes, notes=notes,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_seed( def add_seed(
@@ -376,13 +376,13 @@ class EntryService:
words_num=words, words_num=words,
notes=notes, notes=notes,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_key_value(self, label: str, value: str, *, notes: str = "") -> int: def add_key_value(self, label: str, value: str, *, notes: str = "") -> int:
with self._lock: with self._lock:
idx = self._manager.entry_manager.add_key_value(label, value, notes=notes) idx = self._manager.entry_manager.add_key_value(label, value, notes=notes)
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def add_managed_account( def add_managed_account(
@@ -399,7 +399,7 @@ class EntryService:
index=index, index=index,
notes=notes, notes=notes,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
return idx return idx
def modify_entry( def modify_entry(
@@ -425,17 +425,17 @@ class EntryService:
digits=digits, digits=digits,
value=value, value=value,
) )
self._manager.sync_vault() self._manager.start_background_vault_sync()
def archive_entry(self, entry_id: int) -> None: def archive_entry(self, entry_id: int) -> None:
with self._lock: with self._lock:
self._manager.entry_manager.archive_entry(entry_id) self._manager.entry_manager.archive_entry(entry_id)
self._manager.sync_vault() self._manager.start_background_vault_sync()
def restore_entry(self, entry_id: int) -> None: def restore_entry(self, entry_id: int) -> None:
with self._lock: with self._lock:
self._manager.entry_manager.restore_entry(entry_id) self._manager.entry_manager.restore_entry(entry_id)
self._manager.sync_vault() self._manager.start_background_vault_sync()
def export_totp_entries(self) -> dict: def export_totp_entries(self) -> dict:
with self._lock: with self._lock:

View File

@@ -5,6 +5,7 @@ import time
import toga import toga
from toga.style import Pack from toga.style import Pack
from toga.sources import ListSource
from toga.style.pack import COLUMN, ROW from toga.style.pack import COLUMN, ROW
from seedpass.core.entry_types import EntryType from seedpass.core.entry_types import EntryType
@@ -89,8 +90,10 @@ class MainWindow(toga.Window):
bus.subscribe("vault_locked", self.vault_locked) bus.subscribe("vault_locked", self.vault_locked)
self.last_sync = None self.last_sync = None
self.entry_source = ListSource(["id", "label", "kind", "info1", "info2"])
self.table = toga.Table( self.table = toga.Table(
headings=["ID", "Label", "Kind", "Info 1", "Info 2"], headings=["ID", "Label", "Kind", "Info 1", "Info 2"],
data=self.entry_source,
style=Pack(flex=1), style=Pack(flex=1),
) )
@@ -118,7 +121,7 @@ class MainWindow(toga.Window):
self.refresh_entries() self.refresh_entries()
def refresh_entries(self) -> None: def refresh_entries(self) -> None:
self.table.data = [] self.entry_source.clear()
for idx, label, username, url, _arch in self.entries.list_entries(): for idx, label, username, url, _arch in self.entries.list_entries():
entry = self.entries.retrieve_entry(idx) entry = self.entries.retrieve_entry(idx)
kind = (entry or {}).get("kind", (entry or {}).get("type", "")) kind = (entry or {}).get("kind", (entry or {}).get("type", ""))
@@ -131,7 +134,15 @@ class MainWindow(toga.Window):
info1 = entry.get("value", "") if entry else "" info1 = entry.get("value", "") if entry else ""
else: else:
info1 = str(entry.get("index", "")) if entry else "" info1 = str(entry.get("index", "")) if entry else ""
self.table.data.append((idx, label, kind, info1, info2)) self.entry_source.append(
{
"id": idx,
"label": label,
"kind": kind,
"info1": info1,
"info2": info2,
}
)
# --- Button handlers ------------------------------------------------- # --- Button handlers -------------------------------------------------
def add_entry(self, widget: toga.Widget) -> None: def add_entry(self, widget: toga.Widget) -> None:
@@ -285,9 +296,17 @@ class SearchDialog(toga.Window):
def do_search(self, widget: toga.Widget) -> None: def do_search(self, widget: toga.Widget) -> None:
query = self.query_input.value or "" query = self.query_input.value or ""
results = self.main.entries.search_entries(query) results = self.main.entries.search_entries(query)
self.main.table.data = [] self.main.entry_source.clear()
for idx, label, username, url, _arch in results: for idx, label, username, url, _arch in results:
self.main.table.data.append((idx, label, username or "", url or "")) self.main.entry_source.append(
{
"id": idx,
"label": label,
"kind": "",
"info1": username or "",
"info2": url or "",
}
)
self.close() self.close()

View File

@@ -36,7 +36,7 @@ def test_cli_entry_add_search_sync(monkeypatch):
calls["search"] = (q, kinds) calls["search"] = (q, kinds)
return [(1, "Label", None, None, False)] return [(1, "Label", None, None, False)]
def sync_vault(): def start_background_vault_sync():
calls["sync"] = True calls["sync"] = True
return {"manifest_id": "m", "chunk_ids": [], "delta_ids": []} return {"manifest_id": "m", "chunk_ids": [], "delta_ids": []}
@@ -44,7 +44,8 @@ def test_cli_entry_add_search_sync(monkeypatch):
entry_manager=SimpleNamespace( entry_manager=SimpleNamespace(
add_entry=add_entry, search_entries=search_entries add_entry=add_entry, search_entries=search_entries
), ),
sync_vault=sync_vault, start_background_vault_sync=start_background_vault_sync,
sync_vault=lambda: {"manifest_id": "m", "chunk_ids": [], "delta_ids": []},
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)

View File

@@ -58,6 +58,7 @@ class DummyPM:
"chunk_ids": ["c1"], "chunk_ids": ["c1"],
"delta_ids": [], "delta_ids": [],
} }
self.start_background_vault_sync = lambda *a, **k: self.sync_vault()
self.config_manager = SimpleNamespace( self.config_manager = SimpleNamespace(
load_config=lambda require_pin=False: {"inactivity_timeout": 30}, load_config=lambda require_pin=False: {"inactivity_timeout": 30},
set_inactivity_timeout=lambda v: None, set_inactivity_timeout=lambda v: None,

View File

@@ -115,14 +115,14 @@ def test_entry_add_commands(
called["kwargs"] = kwargs called["kwargs"] = kwargs
return stdout return stdout
def sync_vault(): def start_background_vault_sync():
called["sync"] = True called["sync"] = True
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(**{method: func}), entry_manager=SimpleNamespace(**{method: func}),
parent_seed="seed", parent_seed="seed",
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=sync_vault, start_background_vault_sync=start_background_vault_sync,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", command] + cli_args) result = runner.invoke(app, ["entry", command] + cli_args)

View File

@@ -29,7 +29,7 @@ def test_entry_service_add_entry_and_search():
called["search"] = (q, kinds) called["search"] = (q, kinds)
return [(5, "Example", username, url, False)] return [(5, "Example", username, url, False)]
def sync_vault(): def start_background_vault_sync():
called["sync"] = True called["sync"] = True
username = "user" username = "user"
@@ -38,7 +38,7 @@ def test_entry_service_add_entry_and_search():
entry_manager=SimpleNamespace( entry_manager=SimpleNamespace(
add_entry=add_entry, search_entries=search_entries add_entry=add_entry, search_entries=search_entries
), ),
sync_vault=sync_vault, start_background_vault_sync=start_background_vault_sync,
) )
service = EntryService(pm) service = EntryService(pm)
idx = service.add_entry("Example", 12, username, url) idx = service.add_entry("Example", 12, username, url)

View File

@@ -377,7 +377,7 @@ def test_entry_add(monkeypatch):
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(add_entry=add_entry), entry_manager=SimpleNamespace(add_entry=add_entry),
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=lambda: None, start_background_vault_sync=lambda: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke( result = runner.invoke(
@@ -408,7 +408,7 @@ def test_entry_modify(monkeypatch):
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(modify_entry=modify_entry), entry_manager=SimpleNamespace(modify_entry=modify_entry),
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=lambda: None, start_background_vault_sync=lambda: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", "modify", "1", "--username", "alice"]) result = runner.invoke(app, ["entry", "modify", "1", "--username", "alice"])
@@ -423,7 +423,7 @@ def test_entry_modify_invalid(monkeypatch):
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(modify_entry=modify_entry), entry_manager=SimpleNamespace(modify_entry=modify_entry),
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=lambda: None, start_background_vault_sync=lambda: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", "modify", "1", "--username", "alice"]) result = runner.invoke(app, ["entry", "modify", "1", "--username", "alice"])
@@ -440,7 +440,7 @@ def test_entry_archive(monkeypatch):
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(archive_entry=archive_entry), entry_manager=SimpleNamespace(archive_entry=archive_entry),
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=lambda: None, start_background_vault_sync=lambda: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", "archive", "3"]) result = runner.invoke(app, ["entry", "archive", "3"])
@@ -458,7 +458,7 @@ def test_entry_unarchive(monkeypatch):
pm = SimpleNamespace( pm = SimpleNamespace(
entry_manager=SimpleNamespace(restore_entry=restore_entry), entry_manager=SimpleNamespace(restore_entry=restore_entry),
select_fingerprint=lambda fp: None, select_fingerprint=lambda fp: None,
sync_vault=lambda: None, start_background_vault_sync=lambda: None,
) )
monkeypatch.setattr(cli, "PasswordManager", lambda: pm) monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", "unarchive", "4"]) result = runner.invoke(app, ["entry", "unarchive", "4"])