mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-05 18:30:59 +01:00
Add context menu to search page (#1701)
## Description Adds long clicking search results to show the context menu. It's the same context menu as elsewhere in the app. ### Related issues Closes #1695 ### Testing Emulator to toggle favorite and watched status on various types ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
a350fa2017
commit
ed97fc3009
2 changed files with 211 additions and 50 deletions
|
|
@ -239,6 +239,7 @@ fun DiscoverSearchPage(
|
|||
result = seerrResults,
|
||||
focusRequester = gridFocusRequester,
|
||||
onClickItem = { _, _ -> },
|
||||
onLongClickItem = { _, _ -> },
|
||||
onPlayItem = { _, _ -> },
|
||||
onClickPosition = { position = it.column },
|
||||
onClickDiscover = onClickDiscover,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.github.damontecres.wholphin.ui.main
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.annotation.StringRes
|
||||
|
|
@ -66,15 +67,20 @@ import androidx.tv.material3.Switch
|
|||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||
import com.github.damontecres.wholphin.data.model.SeerrItemType
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.preferences.updateSearchPreferences
|
||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||
import com.github.damontecres.wholphin.services.MediaReportService
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.services.SeerrService
|
||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||
import com.github.damontecres.wholphin.services.deleteItem
|
||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
|
|
@ -84,27 +90,31 @@ import com.github.damontecres.wholphin.ui.cards.GridCard
|
|||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||
import com.github.damontecres.wholphin.ui.cards.ItemRowTitle
|
||||
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
||||
import com.github.damontecres.wholphin.ui.components.ContextMenuProvider
|
||||
import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
|
||||
import com.github.damontecres.wholphin.ui.components.SearchEditTextBox
|
||||
import com.github.damontecres.wholphin.ui.components.TabDetails
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.components.VoiceInputManager
|
||||
import com.github.damontecres.wholphin.ui.components.VoiceSearchButton
|
||||
import com.github.damontecres.wholphin.ui.components.rememberContextMenu
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.detail.CardGrid
|
||||
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
||||
import com.github.damontecres.wholphin.ui.detail.GridItemDetails
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.onMain
|
||||
import com.github.damontecres.wholphin.ui.preferences.SwitchColors
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.SearchRelevance
|
||||
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -115,29 +125,36 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@HiltViewModel
|
||||
class SearchViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
val api: ApiClient,
|
||||
val navigationManager: NavigationManager,
|
||||
private val appPreferences: DataStore<AppPreferences>,
|
||||
private val seerrService: SeerrService,
|
||||
val voiceInputManager: VoiceInputManager,
|
||||
val userPreferencesService: UserPreferencesService,
|
||||
) : ViewModel() {
|
||||
val voiceState = voiceInputManager.state
|
||||
val soundLevel = voiceInputManager.soundLevel
|
||||
val partialResult = voiceInputManager.partialResult
|
||||
private val serverRepository: ServerRepository,
|
||||
private val favoriteWatchManager: FavoriteWatchManager,
|
||||
private val mediaManagementService: MediaManagementService,
|
||||
private val mediaReportService: MediaReportService,
|
||||
) : ViewModel(),
|
||||
ContextMenuProvider {
|
||||
val seerrActive = seerrService.active
|
||||
|
||||
private val _state = MutableStateFlow(SearchState())
|
||||
val state: StateFlow<SearchState> = _state
|
||||
val position = MutableStateFlow(RowColumn(0, 0))
|
||||
|
||||
private var currentQuery: String? = null
|
||||
private var combinedMode = false
|
||||
|
|
@ -234,8 +251,8 @@ class SearchViewModel
|
|||
)
|
||||
val result = api.itemsApi.getItems(request).content
|
||||
val items =
|
||||
(result.items ?: emptyList()).map {
|
||||
BaseItem.from(it, api, false)
|
||||
result.items.map {
|
||||
BaseItem(it, false)
|
||||
}
|
||||
val sorted =
|
||||
items.sortedWith(
|
||||
|
|
@ -271,8 +288,8 @@ class SearchViewModel
|
|||
|
||||
val result = api.itemsApi.getItems(request).content
|
||||
val items =
|
||||
(result.items ?: emptyList()).map {
|
||||
BaseItem.from(it, api, false)
|
||||
result.items.map {
|
||||
BaseItem(it, false)
|
||||
}
|
||||
val sorted =
|
||||
items.sortedWith(
|
||||
|
|
@ -325,10 +342,113 @@ class SearchViewModel
|
|||
addCloseable(voiceInputManager)
|
||||
}
|
||||
|
||||
fun getHints(query: String) {
|
||||
// TODO
|
||||
// api.searchApi.getSearchHints()
|
||||
override fun navigateTo(destination: Destination) {
|
||||
navigationManager.navigateTo(destination)
|
||||
}
|
||||
|
||||
override fun canDelete(
|
||||
item: BaseItem,
|
||||
appPreferences: AppPreferences,
|
||||
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||
|
||||
override fun deleteItem(
|
||||
index: Int,
|
||||
item: BaseItem,
|
||||
) {
|
||||
deleteItem(context, mediaManagementService, item) {
|
||||
viewModelScope.launchDefault {
|
||||
refreshItem(item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshItem(itemId: UUID) {
|
||||
try {
|
||||
val position = position.value
|
||||
val searchResult =
|
||||
if (combinedMode) {
|
||||
state.value.combinedResults
|
||||
} else {
|
||||
when (position.row) {
|
||||
MOVIE_ROW -> state.value.movies
|
||||
SERIES_ROW -> state.value.series
|
||||
EPISODE_ROW -> state.value.episodes
|
||||
COLLECTION_ROW -> state.value.collections
|
||||
ALBUM_ROW -> state.value.albums
|
||||
ARTIST_ROW -> state.value.artists
|
||||
SONG_ROW -> state.value.songs
|
||||
SEERR_ROW -> null
|
||||
else -> null
|
||||
}
|
||||
} ?: return
|
||||
val items = (searchResult as? SearchResult.Success)?.items ?: return
|
||||
|
||||
Timber.v("Item refresh: position=%s", position)
|
||||
val item = items.getOrNull(position.column)
|
||||
// Exact item deleted (eg a movie) or deleted item was within the series
|
||||
if (item != null && item.id == itemId) {
|
||||
val newItem =
|
||||
api.userLibraryApi
|
||||
.getItem(item.id)
|
||||
.content
|
||||
.let { BaseItem(it) }
|
||||
val newList =
|
||||
SearchResult.Success(
|
||||
items.toMutableList().apply {
|
||||
set(position.column, newItem)
|
||||
},
|
||||
)
|
||||
_state.update {
|
||||
if (combinedMode) {
|
||||
it.copy(
|
||||
combinedResults = newList,
|
||||
)
|
||||
} else {
|
||||
when (position.row) {
|
||||
MOVIE_ROW -> it.copy(movies = newList)
|
||||
SERIES_ROW -> it.copy(series = newList)
|
||||
EPISODE_ROW -> it.copy(episodes = newList)
|
||||
COLLECTION_ROW -> it.copy(collections = newList)
|
||||
ALBUM_ROW -> it.copy(albums = newList)
|
||||
ARTIST_ROW -> it.copy(artists = newList)
|
||||
SONG_ROW -> it.copy(songs = newList)
|
||||
SEERR_ROW -> it
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error refreshing item %s", itemId)
|
||||
showToast(context, "Error refreshing")
|
||||
}
|
||||
}
|
||||
|
||||
override fun setWatched(
|
||||
position: Int,
|
||||
itemId: UUID,
|
||||
played: Boolean,
|
||||
) {
|
||||
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
|
||||
favoriteWatchManager.setWatched(itemId, played)
|
||||
refreshItem(itemId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setFavorite(
|
||||
position: Int,
|
||||
itemId: UUID,
|
||||
favorite: Boolean,
|
||||
) {
|
||||
viewModelScope.launch(ExceptionHandler() + WholphinDispatchers.IO) {
|
||||
favoriteWatchManager.setFavorite(itemId, favorite)
|
||||
refreshItem(itemId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isAdministrator(): Boolean = serverRepository.currentUserDto?.policy?.isAdministrator == true
|
||||
|
||||
override fun sendReportFor(itemId: UUID) = mediaReportService.sendReportFor(itemId)
|
||||
}
|
||||
|
||||
sealed interface SearchResult {
|
||||
|
|
@ -374,9 +494,6 @@ private const val SEERR_ROW = SONG_ROW + 1
|
|||
|
||||
private const val COMBINED_ROW = TAB_ROW + 1
|
||||
|
||||
/** Delay for focus to settle after voice search dialog dismisses. */
|
||||
private const val VOICE_RESULT_FOCUS_DELAY_MS = 350L
|
||||
|
||||
@Composable
|
||||
fun SearchPage(
|
||||
userPreferences: UserPreferences,
|
||||
|
|
@ -398,16 +515,22 @@ fun SearchPage(
|
|||
// val query = rememberTextFieldState()
|
||||
var query by rememberSaveable { mutableStateOf("") }
|
||||
val focusRequesters = remember { List(SEERR_ROW + 1) { FocusRequester() } }
|
||||
val tabFocusRequesters = remember { List(2) { FocusRequester() } }
|
||||
|
||||
val seerrActive by viewModel.seerrActive.collectAsState(initial = false)
|
||||
var selectedTab by rememberSaveable { mutableIntStateOf(0) }
|
||||
var showViewOptions by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
var position by rememberPosition(0, 0)
|
||||
var searchClicked by rememberSaveable(query) { mutableStateOf(false) }
|
||||
var immediateSearchQuery by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
|
||||
val position by viewModel.position.collectAsState()
|
||||
|
||||
fun setPosition(pos: RowColumn) {
|
||||
Timber.v("pos=%s", pos)
|
||||
viewModel.position.value = pos
|
||||
}
|
||||
|
||||
val contextMenu = rememberContextMenu(userPreferences, viewModel)
|
||||
|
||||
LifecycleResumeEffect(Unit) {
|
||||
onPauseOrDispose {
|
||||
viewModel.voiceInputManager.stopListening()
|
||||
|
|
@ -427,7 +550,7 @@ fun SearchPage(
|
|||
}
|
||||
|
||||
else -> {
|
||||
delay(750L)
|
||||
delay(750.milliseconds)
|
||||
viewModel.search(query, combinedMode)
|
||||
}
|
||||
}
|
||||
|
|
@ -435,9 +558,13 @@ fun SearchPage(
|
|||
LaunchedEffect(Unit) {
|
||||
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||
}
|
||||
val onClickItem = { index: Int, item: BaseItem ->
|
||||
val onClickItem = { _: Int, item: BaseItem ->
|
||||
viewModel.navigationManager.navigateTo(item.destination())
|
||||
}
|
||||
val onLongClickItem = { rowIndex: Int, index: Int, item: BaseItem ->
|
||||
setPosition(RowColumn(rowIndex, index))
|
||||
contextMenu.showContextMenu(index, item)
|
||||
}
|
||||
val onPlayItem = { _: Int, item: BaseItem ->
|
||||
viewModel.navigationManager.navigateTo(Destination.Playback(item))
|
||||
}
|
||||
|
|
@ -475,10 +602,10 @@ fun SearchPage(
|
|||
selectedTab,
|
||||
seerrActive,
|
||||
) {
|
||||
if (!searchClicked) return@LaunchedEffect
|
||||
if (!searchClicked || position.row > TAB_ROW) return@LaunchedEffect
|
||||
|
||||
withContext(WholphinDispatchers.IO) {
|
||||
// Want to focus on the first successful row after all of the ones before it are finished searching
|
||||
// Want to focus on the first successful row after all the ones before it are finished searching
|
||||
val results =
|
||||
if (isLibraryTab) {
|
||||
if (combinedMode) {
|
||||
|
|
@ -513,7 +640,7 @@ fun SearchPage(
|
|||
} else {
|
||||
SEERR_ROW
|
||||
}
|
||||
position = RowColumn(targetRow, 0)
|
||||
// setPosition(RowColumn(targetRow, 0))
|
||||
onMain { focusRequesters[targetRow].tryRequestFocus() }
|
||||
}
|
||||
}
|
||||
|
|
@ -628,12 +755,15 @@ fun SearchPage(
|
|||
0 -> COMBINED_ROW
|
||||
else -> SEERR_ROW
|
||||
}
|
||||
position = RowColumn(row, 0)
|
||||
setPosition(RowColumn(row, 0))
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp),
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
.onFocusChanged {
|
||||
if (it.hasFocus) setPosition(RowColumn(TAB_ROW, 0))
|
||||
},
|
||||
)
|
||||
}
|
||||
Box(
|
||||
|
|
@ -648,8 +778,11 @@ fun SearchPage(
|
|||
result = state.combinedResults,
|
||||
focusRequester = focusRequesters[COMBINED_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(COMBINED_ROW, index, item)
|
||||
},
|
||||
onPlayItem = onPlayItem,
|
||||
onClickPosition = { position = it },
|
||||
onClickPosition = { setPosition(it) },
|
||||
onClickDiscover = onClickDiscover,
|
||||
positionCallback = positionCallback,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
@ -661,8 +794,9 @@ fun SearchPage(
|
|||
result = state.seerrResults,
|
||||
focusRequester = focusRequesters[SEERR_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { _, _ -> },
|
||||
onPlayItem = onPlayItem,
|
||||
onClickPosition = { position = it },
|
||||
onClickPosition = { setPosition(it) },
|
||||
onClickDiscover = onClickDiscover,
|
||||
positionCallback = positionCallback,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
@ -688,7 +822,10 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[MOVIE_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(MOVIE_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
searchResultRow(
|
||||
|
|
@ -698,7 +835,10 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[SERIES_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(SERIES_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
searchResultRow(
|
||||
|
|
@ -708,13 +848,16 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[EPISODE_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(EPISODE_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardContent = @Composable { index, item, mod, onClick, onLongClick ->
|
||||
EpisodeCard(
|
||||
item = item,
|
||||
onClick = {
|
||||
position = RowColumn(EPISODE_ROW, index)
|
||||
setPosition(RowColumn(EPISODE_ROW, index))
|
||||
onClick.invoke()
|
||||
},
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -730,7 +873,10 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[COLLECTION_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(COLLECTION_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
searchResultRow(
|
||||
|
|
@ -740,13 +886,16 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[ALBUM_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(ALBUM_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardContent = { index, item, mod, onClick, onLongClick ->
|
||||
SeasonCard(
|
||||
item = item,
|
||||
onClick = {
|
||||
position = RowColumn(ALBUM_ROW, index)
|
||||
setPosition(RowColumn(ALBUM_ROW, index))
|
||||
onClick.invoke()
|
||||
},
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -764,13 +913,16 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[ARTIST_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(ARTIST_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardContent = { index, item, mod, onClick, onLongClick ->
|
||||
SeasonCard(
|
||||
item = item,
|
||||
onClick = {
|
||||
position = RowColumn(ARTIST_ROW, index)
|
||||
setPosition(RowColumn(ARTIST_ROW, index))
|
||||
onClick.invoke()
|
||||
},
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -788,13 +940,16 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[SONG_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onClickPosition = { position = it },
|
||||
onLongClickItem = { index, item ->
|
||||
onLongClickItem(SONG_ROW, index, item)
|
||||
},
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardContent = { index, item, mod, onClick, onLongClick ->
|
||||
SeasonCard(
|
||||
item = item,
|
||||
onClick = {
|
||||
position = RowColumn(SONG_ROW, index)
|
||||
setPosition(RowColumn(SONG_ROW, index))
|
||||
onClick.invoke()
|
||||
},
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -812,14 +967,16 @@ fun SearchPage(
|
|||
position = position,
|
||||
focusRequester = focusRequesters[SEERR_ROW],
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { _, _ -> },
|
||||
onClickDiscover = onClickDiscover,
|
||||
onClickPosition = { position = it },
|
||||
onClickPosition = { setPosition(it) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
contextMenu.Compose()
|
||||
}
|
||||
|
||||
if (showViewOptions) {
|
||||
|
|
@ -923,16 +1080,15 @@ fun SearchCombinedResults(
|
|||
result: SearchResult,
|
||||
focusRequester: FocusRequester,
|
||||
onClickItem: (Int, BaseItem) -> Unit,
|
||||
onLongClickItem: (Int, BaseItem) -> Unit,
|
||||
onPlayItem: (Int, BaseItem) -> Unit,
|
||||
onClickPosition: (RowColumn) -> Unit,
|
||||
onClickDiscover: (Int, DiscoverItem) -> Unit,
|
||||
positionCallback: (columns: Int, position: Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (val r = result) {
|
||||
SearchResult.NoQuery -> {
|
||||
Unit
|
||||
}
|
||||
when (result) {
|
||||
SearchResult.NoQuery -> {}
|
||||
|
||||
SearchResult.Searching -> {
|
||||
SearchResultPlaceholder(
|
||||
|
|
@ -945,14 +1101,14 @@ fun SearchCombinedResults(
|
|||
is SearchResult.Error -> {
|
||||
SearchResultPlaceholder(
|
||||
title = stringResource(R.string.results),
|
||||
message = r.ex.localizedMessage ?: "Error occurred during search",
|
||||
message = result.ex.localizedMessage ?: "Error occurred during search",
|
||||
messageColor = MaterialTheme.colorScheme.error,
|
||||
modifier = modifier.padding(16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
is SearchResult.Success -> {
|
||||
if (r.items.isEmpty()) {
|
||||
if (result.items.isEmpty()) {
|
||||
SearchResultPlaceholder(
|
||||
title = stringResource(R.string.results),
|
||||
message = stringResource(R.string.no_results),
|
||||
|
|
@ -960,9 +1116,10 @@ fun SearchCombinedResults(
|
|||
)
|
||||
} else {
|
||||
SearchGrid(
|
||||
items = r.items,
|
||||
items = result.items,
|
||||
focusRequester = focusRequester,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = onLongClickItem,
|
||||
onPlayItem = onPlayItem,
|
||||
onClickPosition = onClickPosition,
|
||||
positionCallback = positionCallback,
|
||||
|
|
@ -981,7 +1138,7 @@ fun SearchCombinedResults(
|
|||
}
|
||||
|
||||
is SearchResult.SuccessSeerr -> {
|
||||
if (r.items.isEmpty()) {
|
||||
if (result.items.isEmpty()) {
|
||||
SearchResultPlaceholder(
|
||||
title = stringResource(R.string.results),
|
||||
message = stringResource(R.string.no_results),
|
||||
|
|
@ -989,9 +1146,10 @@ fun SearchCombinedResults(
|
|||
)
|
||||
} else {
|
||||
SearchGrid(
|
||||
items = r.items,
|
||||
items = result.items,
|
||||
focusRequester = focusRequester,
|
||||
onClickItem = onClickDiscover,
|
||||
onLongClickItem = { _, _ -> },
|
||||
onPlayItem = { _, _ -> },
|
||||
onClickPosition = onClickPosition,
|
||||
positionCallback = positionCallback,
|
||||
|
|
@ -1015,6 +1173,7 @@ private fun <T : CardGridItem> SearchGrid(
|
|||
items: List<T?>,
|
||||
focusRequester: FocusRequester,
|
||||
onClickItem: (Int, T) -> Unit,
|
||||
onLongClickItem: (Int, T) -> Unit,
|
||||
onPlayItem: (Int, T) -> Unit,
|
||||
onClickPosition: (RowColumn) -> Unit,
|
||||
cardContent: @Composable (GridItemDetails<T>) -> Unit,
|
||||
|
|
@ -1033,7 +1192,7 @@ private fun <T : CardGridItem> SearchGrid(
|
|||
onClickPosition.invoke(RowColumn(COMBINED_ROW, index))
|
||||
onClickItem.invoke(index, item)
|
||||
},
|
||||
onLongClickItem = { _, _ -> },
|
||||
onLongClickItem = onLongClickItem,
|
||||
onClickPlay = { index, item ->
|
||||
onClickPosition.invoke(RowColumn(COMBINED_ROW, index))
|
||||
onPlayItem.invoke(index, item)
|
||||
|
|
@ -1056,6 +1215,7 @@ fun LazyListScope.searchResultRow(
|
|||
position: RowColumn,
|
||||
focusRequester: FocusRequester,
|
||||
onClickItem: (Int, BaseItem) -> Unit,
|
||||
onLongClickItem: (Int, BaseItem) -> Unit,
|
||||
onClickPosition: (RowColumn) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onClickDiscover: ((Int, DiscoverItem) -> Unit)? = null,
|
||||
|
|
@ -1108,7 +1268,7 @@ fun LazyListScope.searchResultRow(
|
|||
title = stringResource(title),
|
||||
items = r.items,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { _, _ -> },
|
||||
onLongClickItem = onLongClickItem,
|
||||
modifier = modifier.focusRequester(focusRequester),
|
||||
cardContent = cardContent,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue