feat: route board and card taps to detail screens

This commit is contained in:
2026-03-16 01:33:48 -04:00
parent 5f5a273d7f
commit f5ac01de09
8 changed files with 129 additions and 35 deletions

View File

@@ -11,6 +11,9 @@ import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.longClick
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@@ -53,12 +56,14 @@ class BoardDetailFlowTest {
@Before
fun setUp() {
originalLocale = Locale.getDefault()
Intents.init()
defaultDataSource = FakeBoardDetailDataSource(initialDetail = detailOneList())
BoardDetailActivity.testDataSourceFactory = { defaultDataSource }
}
@After
fun tearDown() {
Intents.release()
BoardDetailActivity.testDataSourceFactory = null
originalLocale?.let { Locale.setDefault(it) }
}
@@ -245,6 +250,29 @@ class BoardDetailFlowTest {
}
}
@Test
fun cardTapNavigatesToCardPlaceholderWithExtras() {
launchBoardDetail()
onView(withText("Card 1")).perform(click())
Intents.intended(hasComponent(CardDetailPlaceholderActivity::class.java.name))
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, "card-1"))
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, "Card 1"))
}
@Test
fun cardTapBlankTitle_usesCardFallbackInPlaceholderExtra() {
defaultDataSource.currentDetail = detailWithCardTitle(" ")
launchBoardDetail()
onView(withId(R.id.cardItemRoot)).perform(click())
Intents.intended(hasComponent(CardDetailPlaceholderActivity::class.java.name))
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, "card-1"))
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, "Card"))
}
private fun launchBoardDetail(): ActivityScenario<BoardDetailActivity> {
val intent = Intent(
androidx.test.core.app.ApplicationProvider.getApplicationContext(),
@@ -353,5 +381,24 @@ class BoardDetailFlowTest {
),
)
}
fun detailWithCardTitle(title: String): BoardDetail {
return detailOneList().copy(
lists = listOf(
BoardListDetail(
id = "list-1",
title = "To Do",
cards = listOf(
BoardCardSummary(
id = "card-1",
title = title,
tags = emptyList(),
dueAtEpochMillis = null,
),
),
),
),
)
}
}
}

View File

@@ -46,7 +46,7 @@ class BoardsFlowTest {
}
@Test
fun boardTapNavigatesToDetailPlaceholderWithExtras() {
fun boardTapNavigatesToBoardDetailActivity() {
MainActivity.dependencies.apiClientFactory = {
FakeBoardsApiClient(
boards = mutableListOf(BoardSummary("1", "Alpha")),
@@ -58,9 +58,9 @@ class BoardsFlowTest {
onView(withText("Alpha")).perform(click())
Intents.intended(hasComponent(BoardDetailPlaceholderActivity::class.java.name))
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_ID, "1"))
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, "Alpha"))
Intents.intended(hasComponent(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity::class.java.name))
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_ID, "1"))
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_TITLE, "Alpha"))
}
@Test
@@ -79,8 +79,8 @@ class BoardsFlowTest {
onView(withId(R.id.useTemplateChip)).perform(click())
onView(withId(android.R.id.button1)).inRoot(isDialog()).perform(click())
Intents.intended(hasComponent(BoardDetailPlaceholderActivity::class.java.name))
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, "Roadmap"))
Intents.intended(hasComponent(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity::class.java.name))
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_TITLE, "Roadmap"))
}
@Test

View File

@@ -12,7 +12,7 @@
android:usesCleartextTraffic="true"
android:theme="@style/Theme.Kanbn4Droid">
<activity
android:name=".BoardDetailPlaceholderActivity"
android:name=".CardDetailPlaceholderActivity"
android:exported="false" />
<activity
android:name=".boarddetail.BoardDetailActivity"

View File

@@ -1,24 +0,0 @@
package space.hackenslacker.kanbn4droid.app
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class BoardDetailPlaceholderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_board_detail_placeholder)
val boardTitle = intent.getStringExtra(EXTRA_BOARD_TITLE).orEmpty()
val boardId = intent.getStringExtra(EXTRA_BOARD_ID).orEmpty()
val titleView: TextView = findViewById(R.id.boardDetailPlaceholderTitle)
titleView.text = getString(R.string.board_detail_placeholder_title, boardTitle, boardId)
}
companion object {
const val EXTRA_BOARD_ID = "extra_board_id"
const val EXTRA_BOARD_TITLE = "extra_board_title"
}
}

View File

@@ -35,6 +35,7 @@ import space.hackenslacker.kanbn4droid.app.boards.BoardsRepository
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiEvent
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiState
import space.hackenslacker.kanbn4droid.app.boards.BoardsViewModel
import space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity
class BoardsActivity : AppCompatActivity() {
private lateinit var sessionStore: SessionStore
@@ -245,9 +246,9 @@ class BoardsActivity : AppCompatActivity() {
private fun navigateToBoard(board: BoardSummary) {
startActivity(
Intent(this, BoardDetailPlaceholderActivity::class.java)
.putExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_ID, board.id)
.putExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, board.title),
Intent(this, BoardDetailActivity::class.java)
.putExtra(BoardDetailActivity.EXTRA_BOARD_ID, board.id)
.putExtra(BoardDetailActivity.EXTRA_BOARD_TITLE, board.title),
)
}

View File

@@ -0,0 +1,24 @@
package space.hackenslacker.kanbn4droid.app
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class CardDetailPlaceholderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_card_detail_placeholder)
val cardId = intent.getStringExtra(EXTRA_CARD_ID).orEmpty()
val cardTitle = intent.getStringExtra(EXTRA_CARD_TITLE).orEmpty()
val titleView: TextView = findViewById(R.id.cardDetailPlaceholderTitle)
titleView.text = "$cardTitle\n(id: $cardId)"
}
companion object {
const val EXTRA_CARD_ID = "extra_card_id"
const val EXTRA_CARD_TITLE = "extra_card_title"
}
}

View File

@@ -1,5 +1,6 @@
package space.hackenslacker.kanbn4droid.app.boarddetail
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@@ -17,6 +18,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.launch
import space.hackenslacker.kanbn4droid.app.MainActivity
import space.hackenslacker.kanbn4droid.app.CardDetailPlaceholderActivity
import space.hackenslacker.kanbn4droid.app.R
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
import space.hackenslacker.kanbn4droid.app.auth.HttpKanbnApiClient
@@ -157,7 +159,21 @@ class BoardDetailActivity : AppCompatActivity() {
viewModel.events.collect { event ->
when (event) {
is BoardDetailUiEvent.NavigateToCardPlaceholder -> {
Snackbar.make(pager, getString(R.string.board_detail_card_detail_coming_soon), Snackbar.LENGTH_SHORT).show()
val cardTitle = viewModel.uiState.value.boardDetail
?.lists
.orEmpty()
.asSequence()
.flatMap { list -> list.cards.asSequence() }
.firstOrNull { card -> card.id == event.cardId }
?.title
.orEmpty()
.trim()
.ifBlank { "Card" }
startActivity(
Intent(this@BoardDetailActivity, CardDetailPlaceholderActivity::class.java)
.putExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, event.cardId)
.putExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, cardTitle),
)
}
is BoardDetailUiEvent.ShowServerError -> {

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="@+id/cardDetailPlaceholderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/cardDetailPlaceholderSubtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="@string/board_detail_card_detail_coming_soon"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cardDetailPlaceholderTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>