feat: clean up stale announces, add PHONE NodeType and cross-link buttons#581
feat: clean up stale announces, add PHONE NodeType and cross-link buttons#581torlando-tech merged 5 commits intomainfrom
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR introduces four coordinated features: stale announce cleanup (DAO query + service lifecycle trigger), a new The overall implementation is solid — the reactive Key issues found:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant RS as ReticulumService.onCreate()
participant SPM as ServicePersistenceManager
participant DAO as AnnounceDao
participant DB as SQLite (announces)
RS->>SPM: cleanupStaleAnnounces()
SPM->>SPM: cutoffTime = now - 30 days
SPM->>DAO: deleteStaleAnnounces(cutoffTime)
DAO->>DB: DELETE WHERE lastSeenTimestamp < cutoff\nAND isFavorite = 0\nAND hash NOT IN contacts
DB-->>DAO: deleted row count
DAO-->>SPM: Int
SPM->>SPM: Log.d if deleted > 0
participant ADS as AnnounceDetailScreen
participant VM as AnnounceStreamViewModel
participant AR as AnnounceRepository
ADS->>VM: getLinkedAnnouncesFlow(destinationHash)
VM->>AR: getAnnounceFlow(destinationHash)
AR-->>VM: Flow<Announce?>
VM->>VM: flatMapLatest: computeIdentityHash(announce.publicKey)
VM->>AR: getLinkedAnnouncesFlow(identityHash, excludeHash)
AR->>DAO: getAnnouncesByIdentityHashExcludingFlow(identityHash, excludeHash)
DAO-->>AR: Flow<List<AnnounceEntity>>
AR-->>VM: Flow<List<Announce>>
VM-->>ADS: Flow<List<Announce>>
ADS->>ADS: Show "View messaging destination" or\n"View telephony destination" button
|
app/src/main/java/com/lxmf/messenger/viewmodel/AnnounceStreamViewModel.kt
Show resolved
Hide resolved
data/src/main/java/com/lxmf/messenger/data/repository/AnnounceRepository.kt
Outdated
Show resolved
Hide resolved
82d160b to
9e9d21d
Compare
9e9d21d to
3c4b9c4
Compare
| * @param maxAgeMillis Maximum age in milliseconds; announces last seen longer ago than this are deleted | ||
| * @return Number of deleted rows | ||
| */ | ||
| suspend fun deleteStaleAnnounces(maxAgeMillis: Long): Int = announceDao.deleteStaleAnnounces(System.currentTimeMillis() - maxAgeMillis) |
There was a problem hiding this comment.
deleteStaleAnnounces and getLinkedAnnounces are unused dead code
Both repository wrapper methods added in this PR are never called from production code:
deleteStaleAnnounces(maxAgeMillis: Long)(line 401) —ServicePersistenceManager.cleanupStaleAnnounces()callsannounceDao.deleteStaleAnnounces(cutoffTime)directly, bypassing this method entirely.getLinkedAnnounces(identityHash, excludeHash)(lines 269–271) — only the reactivegetLinkedAnnouncesFlowvariant is actually used inAnnounceStreamViewModel. The suspend overload has no callers.
Since both were added in this PR, either they should be wired up or removed. If ServicePersistenceManager is intentionally bypassing the repository layer (which is consistent with its existing pattern), then at minimum deleteStaleAnnounces in AnnounceRepository should be removed to avoid confusion about the intended cleanup path.
Prompt To Fix With AI
This is a comment left during a code review.
Path: data/src/main/java/com/lxmf/messenger/data/repository/AnnounceRepository.kt
Line: 401
Comment:
**`deleteStaleAnnounces` and `getLinkedAnnounces` are unused dead code**
Both repository wrapper methods added in this PR are never called from production code:
- `deleteStaleAnnounces(maxAgeMillis: Long)` (line 401) — `ServicePersistenceManager.cleanupStaleAnnounces()` calls `announceDao.deleteStaleAnnounces(cutoffTime)` directly, bypassing this method entirely.
- `getLinkedAnnounces(identityHash, excludeHash)` (lines 269–271) — only the reactive `getLinkedAnnouncesFlow` variant is actually used in `AnnounceStreamViewModel`. The suspend overload has no callers.
Since both were added in this PR, either they should be wired up or removed. If `ServicePersistenceManager` is intentionally bypassing the repository layer (which is consistent with its existing pattern), then at minimum `deleteStaleAnnounces` in `AnnounceRepository` should be removed to avoid confusion about the intended cleanup path.
How can I resolve this? If you propose a fix, please make it concise.Drops the deprecated `call.audio` meshchat aspect and adds `lxst.telephony` as a first-class `NodeType.PHONE` throughout the stack: - python: register `lxst.telephony` announce handler so RNS delivers these announces to Kotlin (previously they were silently dropped) - model: add `PHONE` to `NodeType` enum - NodeTypeDetector: `lxst.telephony` → `NodeType.PHONE`; remove `call.audio` case - AnnounceDao: add `getAnnouncesByIdentityHashExcluding` for identity cross-linking - AnnounceRepository: expose `getLinkedAnnounces()` for cross-link lookups - AnnounceStreamViewModel: type filter now uses `PHONE` instead of PEER-for-audio hack; `isAudioAnnounce` checks `nodeType == PHONE`; add `getLinkedAnnouncesFlow()` - PeerCard: add "Phone" badge for PHONE nodeType; remove AudioBadge composable - AnnounceStreamScreen: `lxst.telephony` falls through to NodeTypeBadge; PHONE excluded from type-filter checkboxes (controlled by audio toggle) - AnnounceDetailScreen: update aspect subtitle; add `onViewAnnounce` param; show symmetric cross-link buttons between telephony and messaging destinations - MainActivity: wire `onViewAnnounce` to announce detail navigation No Room migration needed — NodeType is stored as a plain string; old `call.audio` rows remain as `nodeType="NODE"` and age out naturally. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Announces table grows unbounded as peers are discovered. Add a cleanup query that deletes announces with lastSeenTimestamp > 30 days old, preserving favorites and contacts across all identities. Triggered once per service lifecycle in ReticulumService.onCreate(). Closes #273 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ViewModelTest Reset Dispatchers.Main before canceling viewModelScope in tearDown. This ensures IO coroutine cleanup (geocoder check on Dispatchers.IO) doesn't route cancellation exceptions through the test dispatcher, which caused intermittent UncaughtExceptionsBeforeTest failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use aspect consistently for cross-link detection (lxst.telephony) - Make dead PHONE branch explicit with error() guard - Restore red badge color for PHONE to distinguish from NODE - Update stale call.audio comment to lxst.telephony Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace cold flow{} in getLinkedAnnouncesFlow with flatMapLatest over
Room's reactive queries so cross-link buttons appear live when a
linked announce arrives after the detail screen is already open
- Add reactive DAO/repository methods for identity-hash-based lookups
- Fix misleading deleteStaleAnnounces docstring (parameter is a duration,
not a pre-computed timestamp)
- Add missing onViewAnnounce callback to MainActivity's AnnounceDetailScreen
call site (lost during rebase conflict resolution)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3c4b9c4 to
d604c4d
Compare
Summary
deleteStaleAnnounces()DAO query that removes announces withlastSeenTimestampolder than 30 days, preserving favorites and contacts (across all identities). Triggered once per service lifecycle inReticulumService.onCreate()call.audioaspect withlxst.telephony, addNodeType.PHONEenum variant, register Python announce handler forlxst.telephonylxmf.deliveryandlxst.telephonyannounces (matched by identity hash)Dispatchers.resetMain()beforeviewModelScope.cancel()inOfflineMapDownloadViewModelTestTest plan
AnnounceDaoTest— 6 new tests for stale cleanup (old deletion, recent/favorite/contact preservation, empty table, correct count)NodeTypeDetectorTest— updated forlxst.telephony→PHONEAnnounceDetailScreenTest— updated foronViewAnnounceandgetLinkedAnnouncesFlow:app:compileNoSentryDebugKotlinbuilds cleanCloses #273
🤖 Generated with Claude Code