feat(data): unarchive() primitive on ConversationRepository (#96)#112
Conversation
Adds the inverse of archive() so the Archived Discussions UI (#94) can restore an archived discussion. The Fake implementation clears the archived flag; behavior mirrors archive() (unknown ID → IllegalArgumentException, idempotent on already-unarchived conversations). Closes #96 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Review: #96Decision: PASS FindingsNone. SummaryTrivial inverse of Interface change is purely additive. All 6 Tests follow the
Spec doc at No |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
What
Adds
suspend fun unarchive(conversationId: String)toConversationRepositoryand implements it inFakeConversationRepositoryas the inverse ofarchive(). Clears thearchivedflag so the conversation reappears in theDiscussions(orChannels) stream and leavesArchived.Issue
Closes #96
Architecture compliance
Follows the spec at
docs/specs/architecture/96-unarchive-primitive.mdexactly:archive()(line 32) — inverse pairing visible at a glance.archive()structure (atomicstate.update,throw unknown(id)for unknown IDs,copy(archived = false)).copy(archived = false)produces an equalConversationvalue;StateFlowdeduplicates equal emissions. Matches the establishedarchive()convention.Conversation.archivedalready exists from feat(data): retain conversations on archive() + expose archived filter + seed #93).Testing
Three new unit tests in
FakeConversationRepositoryTest, mirroring the existingarchive_*cluster:unarchive_movesConversation_from_Archived_to_Discussions_andRetainsInStore— the AC test, using the existingseed-discussion-archivedseed.unarchive_onUnknownId_throws—IllegalArgumentExceptionon unknown ID.unarchive_isIdempotent— two consecutive calls leave the conversation inDiscussionsexactly once.TDD: RED first (compile failure on unresolved
unarchive), then GREEN after adding interface + impl.Test fakes in
ChannelListViewModelTestandDiscussionListViewModelTestgot matchingTODO("not used")stubs to satisfy the new interface member — same pattern used forarchive()in those files.Build status
./gradlew :app:testDebugUnitTest— pass./gradlew :app:lint— pass (no new findings)./gradlew :app:assembleDebug— pass./gradlew :app:connectedAndroidTest— not run (no device/emulator connected in dispatcher env); change is data-layer only, no UI surface.🤖 Generated with Claude Code