feat(ui): Archived Discussions screen + ViewModel + Settings entry (#94)#115
Conversation
Adds an "Archived discussions" entry under Settings → Storage that navigates to a new screen listing archived (isPromoted = false, archived = true) conversations observed from ConversationRepository. Long-press a row to reveal "Restore", which invokes ConversationRepository.unarchive(id) and drops the row from the next emission. Closes #94 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Review: #94Decision: FAIL Findings
Other observations (none blocking)
SummaryOne blast-radius miss: the new required |
…Test Fixes the compileDebugAndroidTestKotlin failure flagged by code review on #115: the new required SettingsScreen parameter wasn't propagated to the two instrumented-test setContent call sites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Review: #94Decision: PASS Findings
SummaryImplementation matches the spec verbatim: VM uses VM tests cover all the spec's required scenarios (Loading initial, Archived filter passed, archived-channels dropped, empty source, all-channels-empty, restore-id captured, back-doesn't-dispatch, error path with raw message, non-blank fallback). Three
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
What
ArchivedDiscussionsScreen+ArchivedDiscussionsViewModelunderui/settings/. MVI shape per CLAUDE.md: sealedUiState(Loading | Empty | Loaded | Error) and sealedEvent(RestoreRequested | BackTapped).Routes.ARCHIVED_DISCUSSIONSwired inMainActivity.PyryNavHost.viewModel { ArchivedDiscussionsViewModel(get()) }line inAppModule.archived_discussions_title,archived_discussions_empty,archived_discussions_settings_row,restore_action. Reusedcd_back.VM consumes
repository.observeConversations(ConversationFilter.Archived)and applies a!isPromotedpost-filter (so archived channels are excluded; only archived discussions show).RestoreRequested(id)firesviewModelScope.launch { repository.unarchive(id) }— the row drops naturally from the next upstream emission.Screen reuses the existing
ConversationRowwithModifier.alpha(0.65f)for the muted/archived treatment (mirrors the discussion-row treatment inDiscussionListScreen). Long-press →DropdownMenuwith one "Restore" item — exactly one gesture per ticket guidance.Issue
Closes #94
Blocked-by #93 and #96 are both merged; the archived stream and
unarchiveprimitive are live.Testing
./gradlew test— passes. NewArchivedDiscussionsViewModelTestcovers: initial Loading, Archived filter is requested, post-filter drops promoted-archived, Empty (empty list + only-channels),RestoreRequestedcallsunarchivewith the right id,BackTappedis a no-op for the repo, stream-error →Error, null/blank exception message → non-blank fallback../gradlew lint— clean../gradlew assembleDebug— succeeds../gradlew connectedAndroidTest— not run (no device/emulator in this dev env).Architecture compliance
state: UiState, onEvent: (Event) -> Unitshape.StateFlow<UiState>via.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000L), Loading)— mirrorsDiscussionListViewModel.DiscussionListScreen's convention.BackTappedisUnitin the VM; back navigation handled by NavHost (matchesDiscussionListEvent.BackTapped).MutableStateFlow.updatere-emits and the filter drops the row.Open items (per spec, deferred)
archivedAttimestamp field onConversationis a follow-up (separate ticket when the 30-day auto-archive worker lands). For now the trailing slot rendersformatRelativeTime(lastUsedAt), which for the seeded archived discussion prints "Apr 15, 2026" — acceptable as the "archive date" surface for this slice.SettingsViewModelStateFlowand is deferred per the spec.Note on spotless
./gradlew spotlessCheckreports one pre-existing violation inapp/src/main/java/de/pyryco/mobile/ui/settings/ThemePickerDialog.kt:48(chained-call line-break) that exists onmainand is not in this diff. Per scope discipline (CLAUDE.md "Bug Found Out of Scope") I left it alone — happy to file as a separate cleanup ticket on request.🤖 Generated with Claude Code