diff --git a/app/database.py b/app/database.py index 3544f94..b11b626 100644 --- a/app/database.py +++ b/app/database.py @@ -116,7 +116,7 @@ class Database: def get_contacts(self) -> List[Dict]: with self._connect() as conn: rows = conn.execute( - "SELECT * FROM contacts ORDER BY last_seen DESC" + "SELECT * FROM contacts WHERE source != 'deleted' ORDER BY last_seen DESC" ).fetchall() return [dict(r) for r in rows] @@ -147,9 +147,14 @@ class Database: return dict(row) if row else None def delete_contact(self, public_key: str) -> bool: + """Soft-delete: mark as 'deleted' instead of removing. + + Keeps the row so FK references in direct_messages stay intact. + upsert_contact() overwrites source on re-add, auto-undeleting. + """ with self._connect() as conn: cursor = conn.execute( - "DELETE FROM contacts WHERE public_key = ?", + "UPDATE contacts SET source = 'deleted', lastmod = datetime('now') WHERE public_key = ?", (public_key.lower(),) ) return cursor.rowcount > 0 diff --git a/app/device_manager.py b/app/device_manager.py index 4e5d4d2..7798578 100644 --- a/app/device_manager.py +++ b/app/device_manager.py @@ -1000,14 +1000,13 @@ class DeviceManager: return self.db.get_contacts() # return cached def delete_contact(self, pubkey: str) -> Dict: - """Delete a contact from device. Keep DB record to preserve DM history.""" + """Delete a contact from device and soft-delete in database.""" if not self.is_connected: return {'success': False, 'error': 'Device not connected'} try: self.execute(self.mc.commands.remove_contact(pubkey)) - # Don't delete from DB — ON DELETE SET NULL would orphan all DMs. - # Contact stays in DB for historical reference; upsert updates on re-add. + self.db.delete_contact(pubkey) # soft-delete: sets source='deleted' # Also remove from in-memory contacts cache if self.mc.contacts and pubkey in self.mc.contacts: del self.mc.contacts[pubkey]