Skip to content

Commit 7705ebe

Browse files
committed
Address PR comments
1 parent 8f43073 commit 7705ebe

7 files changed

Lines changed: 46 additions & 11 deletions

File tree

modules/services/localization/src/main/res/values/strings.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,9 @@
271271
<string name="tv_up_next_empty_title">Nothing queued up</string>
272272
<string name="tv_up_next_empty_subtitle">Add episodes to Up Next and they’ll play in order, back to back</string>
273273
<string name="tv_up_next_empty_action_title">Add episodes</string>
274-
<string name="tv_podcasts_empty_title">Time to fill this up</string>
275-
<string name="tv_podcasts_empty_subtitle">Followed podcasts show up here, ready to play.</string>
276-
<string name="tv_podcasts_empty_action_title">Discover podcasts</string>
274+
<string name="tv_your_podcasts_empty_title">Time to fill this up</string>
275+
<string name="tv_your_podcasts_empty_subtitle">Followed podcasts show up here, ready to play.</string>
276+
<string name="tv_your_podcasts_empty_action_title">Discover podcasts</string>
277277
<string name="unavailable">Unavailable</string>
278278
<string name="browse_podcasts">Browse podcasts</string>
279279
<string name="pocket_casts_plus_badge">Pocket Casts Plus badge</string>

tv/src/main/java/au/com/shiftyjelly/pocketcasts/component/TvPodcastTile.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fun TvPodcastTile(
2222
podcastTitle: String,
2323
onClick: () -> Unit,
2424
modifier: Modifier = Modifier,
25-
imageModifier: Modifier = Modifier.width(123.dp),
25+
imageModifier: Modifier = Modifier,
2626
) {
2727
TvTile(
2828
onClick = onClick,
@@ -47,6 +47,7 @@ private fun TvPodcastTilePreview() {
4747
artworkUrl = "",
4848
podcastTitle = "Sample Podcast",
4949
onClick = {},
50+
imageModifier = Modifier.width(123.dp),
5051
)
5152
}
5253
}

tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvHomeScreen.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.Spacer
88
import androidx.compose.foundation.layout.fillMaxSize
99
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.width
1011
import androidx.compose.foundation.lazy.LazyColumn
1112
import androidx.compose.runtime.Composable
1213
import androidx.compose.runtime.getValue
@@ -146,6 +147,7 @@ private fun TvHomeRows(
146147
artworkUrl = podcast.artworkUrl,
147148
podcastTitle = podcast.title,
148149
onClick = {},
150+
imageModifier = Modifier.width(123.dp),
149151
)
150152
}
151153
}

tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvTabPlaceholder.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box
66
import androidx.compose.foundation.layout.Spacer
77
import androidx.compose.foundation.layout.fillMaxSize
88
import androidx.compose.foundation.layout.height
9+
import androidx.compose.foundation.layout.width
910
import androidx.compose.foundation.lazy.LazyColumn
1011
import androidx.compose.runtime.Composable
1112
import androidx.compose.ui.Modifier
@@ -79,6 +80,7 @@ fun TvTabPlaceholder(
7980
artworkUrl = "https://picsum.photos/seed/rec$index/272/272",
8081
podcastTitle = "Podcast $index",
8182
onClick = {},
83+
imageModifier = Modifier.width(123.dp),
8284
)
8385
}
8486
}
@@ -92,6 +94,7 @@ fun TvTabPlaceholder(
9294
artworkUrl = "https://picsum.photos/seed/liked$index/272/272",
9395
podcastTitle = "Podcast $index",
9496
onClick = {},
97+
imageModifier = Modifier.width(123.dp),
9598
)
9699
}
97100
}

tv/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/TvYourPodcastsScreen.kt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ import androidx.compose.foundation.layout.widthIn
2020
import androidx.compose.foundation.lazy.grid.GridCells
2121
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
2222
import androidx.compose.foundation.lazy.grid.itemsIndexed
23+
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
2324
import androidx.compose.runtime.Composable
2425
import androidx.compose.runtime.LaunchedEffect
2526
import androidx.compose.runtime.getValue
26-
import androidx.compose.runtime.mutableIntStateOf
27+
import androidx.compose.runtime.mutableStateOf
2728
import androidx.compose.runtime.remember
2829
import androidx.compose.runtime.saveable.rememberSaveable
2930
import androidx.compose.runtime.setValue
@@ -117,10 +118,12 @@ private fun TvYourPodcastsGrid(
117118
color = Color.White,
118119
modifier = Modifier.padding(start = 32.dp, top = 8.dp, bottom = 10.dp),
119120
)
120-
var lastFocusedIndex by rememberSaveable(podcasts.size) { mutableIntStateOf(0) }
121+
val gridState = rememberLazyGridState()
122+
var lastFocusedUuid by rememberSaveable { mutableStateOf<String?>(null) }
121123
val focusRequesters = remember(podcasts.size) { List(podcasts.size) { FocusRequester() } }
122124

123125
LazyVerticalGrid(
126+
state = gridState,
124127
columns = GridCells.Fixed(GRID_COLUMNS),
125128
horizontalArrangement = Arrangement.spacedBy(16.dp),
126129
verticalArrangement = Arrangement.spacedBy(16.dp),
@@ -129,7 +132,11 @@ private fun TvYourPodcastsGrid(
129132
.focusGroup()
130133
.focusProperties {
131134
onEnter = {
132-
focusRequesters.getOrNull(lastFocusedIndex)?.requestFocus()
135+
val visible = gridState.layoutInfo.visibleItemsInfo
136+
val target = podcasts.indexOfFirst { it.uuid == lastFocusedUuid }
137+
.takeIf { index -> index >= 0 && visible.any { it.index == index } }
138+
?: visible.firstOrNull()?.index
139+
target?.let { focusRequesters.getOrNull(it)?.requestFocus() }
133140
}
134141
},
135142
) {
@@ -146,7 +153,7 @@ private fun TvYourPodcastsGrid(
146153
.focusRequester(focusRequesters[index])
147154
.onFocusChanged { focusState ->
148155
if (focusState.hasFocus) {
149-
lastFocusedIndex = index
156+
lastFocusedUuid = podcast.uuid
150157
}
151158
},
152159
)
@@ -170,14 +177,14 @@ private fun TvYourPodcastsEmpty(
170177
) {
171178
Column(horizontalAlignment = Alignment.CenterHorizontally) {
172179
Text(
173-
text = stringResource(LR.string.tv_podcasts_empty_title),
180+
text = stringResource(LR.string.tv_your_podcasts_empty_title),
174181
style = TvTextStyles.ScreenTitle,
175182
color = Color.White,
176183
textAlign = TextAlign.Center,
177184
)
178185
Spacer(modifier = Modifier.height(12.dp))
179186
Text(
180-
text = stringResource(LR.string.tv_podcasts_empty_subtitle),
187+
text = stringResource(LR.string.tv_your_podcasts_empty_subtitle),
181188
style = MaterialTheme.typography.bodyLarge,
182189
color = TvColors.TextSecondary,
183190
textAlign = TextAlign.Center,
@@ -189,7 +196,7 @@ private fun TvYourPodcastsEmpty(
189196
colors = TvButtonDefaults.filledButtonColors(),
190197
modifier = Modifier.focusRequester(focusRequester),
191198
) {
192-
Text(stringResource(LR.string.tv_podcasts_empty_action_title))
199+
Text(stringResource(LR.string.tv_your_podcasts_empty_action_title))
193200
}
194201
}
195202
}

tv/src/main/java/au/com/shiftyjelly/pocketcasts/podcasts/TvYourPodcastsViewModel.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import au.com.shiftyjelly.pocketcasts.models.entity.Podcast
66
import au.com.shiftyjelly.pocketcasts.models.type.PodcastsSortType
7+
import au.com.shiftyjelly.pocketcasts.repositories.di.DefaultDispatcher
78
import au.com.shiftyjelly.pocketcasts.repositories.podcast.PodcastManager
89
import dagger.hilt.android.lifecycle.HiltViewModel
910
import javax.inject.Inject
1011
import kotlin.time.Duration.Companion.milliseconds
12+
import kotlinx.coroutines.CoroutineDispatcher
1113
import kotlinx.coroutines.flow.SharingStarted
1214
import kotlinx.coroutines.flow.StateFlow
1315
import kotlinx.coroutines.flow.WhileSubscribed
16+
import kotlinx.coroutines.flow.flowOn
1417
import kotlinx.coroutines.flow.map
1518
import kotlinx.coroutines.flow.stateIn
1619

1720
@HiltViewModel
1821
class TvYourPodcastsViewModel @Inject constructor(
1922
private val podcastManager: PodcastManager,
23+
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
2024
) : ViewModel() {
2125

2226
val uiState: StateFlow<TvYourPodcastsUiState> = podcastManager.findSubscribedFlow()
@@ -28,6 +32,7 @@ class TvYourPodcastsViewModel @Inject constructor(
2832
TvYourPodcastsUiState.Loaded(sorted)
2933
}
3034
}
35+
.flowOn(defaultDispatcher)
3136
.stateIn(
3237
viewModelScope,
3338
SharingStarted.WhileSubscribed(stopTimeout = 300.milliseconds),

tv/src/test/java/au/com/shiftyjelly/pocketcasts/podcasts/TvYourPodcastsViewModelTest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,25 @@ class TvYourPodcastsViewModelTest {
5353
}
5454
}
5555

56+
@Test
57+
fun `unsubscribing the last podcast falls back to the empty state`() = runTest {
58+
val viewModel = createViewModel()
59+
val apple = podcast("apple", "Apple Cast")
60+
61+
viewModel.uiState.test {
62+
assertEquals(TvYourPodcastsUiState.Loading, awaitItem())
63+
64+
subscribed.emit(listOf(apple))
65+
assertEquals(TvYourPodcastsUiState.Loaded(listOf(apple)), awaitItem())
66+
67+
subscribed.emit(emptyList())
68+
assertEquals(TvYourPodcastsUiState.Empty, awaitItem())
69+
}
70+
}
71+
5672
private fun createViewModel() = TvYourPodcastsViewModel(
5773
podcastManager = podcastManager,
74+
defaultDispatcher = coroutineRule.testDispatcher,
5875
)
5976

6077
private fun podcast(uuid: String, title: String) = Podcast(uuid = uuid, title = title)

0 commit comments

Comments
 (0)