mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Small optimizations to home page & nav drawer (#743)
## Description This PR has several micro optimizations to the home page & nav drawer. Basically it computes text off of the main thread and removes redundant calculations.
This commit is contained in:
parent
0639a7a1da
commit
fc0de2144d
7 changed files with 191 additions and 243 deletions
|
|
@ -6,6 +6,7 @@ import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import com.github.damontecres.wholphin.ui.DateFormatter
|
import com.github.damontecres.wholphin.ui.DateFormatter
|
||||||
|
import com.github.damontecres.wholphin.ui.abbreviateNumber
|
||||||
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
||||||
import com.github.damontecres.wholphin.ui.dot
|
import com.github.damontecres.wholphin.ui.dot
|
||||||
|
|
@ -88,6 +89,15 @@ data class BaseItem(
|
||||||
@Transient
|
@Transient
|
||||||
val ui =
|
val ui =
|
||||||
BaseItemUi(
|
BaseItemUi(
|
||||||
|
episodeCornerText =
|
||||||
|
data.indexNumber?.let { "E$it" }
|
||||||
|
?: data.premiereDate?.let(::formatDateTime),
|
||||||
|
episdodeUnplayedCornerText =
|
||||||
|
data.indexNumber?.let { "E$it" }
|
||||||
|
?: data.userData
|
||||||
|
?.unplayedItemCount
|
||||||
|
?.takeIf { it > 0 }
|
||||||
|
?.let { abbreviateNumber(it) },
|
||||||
quickDetails =
|
quickDetails =
|
||||||
buildAnnotatedString {
|
buildAnnotatedString {
|
||||||
val details =
|
val details =
|
||||||
|
|
@ -191,5 +201,7 @@ val BaseItemDto.aspectRatioFloat: Float? get() = width?.let { w -> height?.let {
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class BaseItemUi(
|
data class BaseItemUi(
|
||||||
|
val episodeCornerText: String?,
|
||||||
|
val episdodeUnplayedCornerText: String?,
|
||||||
val quickDetails: AnnotatedString,
|
val quickDetails: AnnotatedString,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.isSpecified
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.tv.material3.Card
|
import androidx.tv.material3.Card
|
||||||
import androidx.tv.material3.CardDefaults
|
import androidx.tv.material3.CardDefaults
|
||||||
|
|
@ -40,7 +41,6 @@ import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.Cards
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,17 +63,19 @@ fun BannerCard(
|
||||||
) {
|
) {
|
||||||
val imageUrlService = LocalImageUrlService.current
|
val imageUrlService = LocalImageUrlService.current
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val imageUrl =
|
|
||||||
remember(item, cardHeight) {
|
|
||||||
if (item != null) {
|
|
||||||
val fillHeight =
|
val fillHeight =
|
||||||
if (cardHeight != Dp.Unspecified) {
|
remember(cardHeight) {
|
||||||
|
if (cardHeight.isSpecified) {
|
||||||
with(density) {
|
with(density) {
|
||||||
cardHeight.roundToPx()
|
cardHeight.roundToPx()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
val imageUrl =
|
||||||
|
remember(item, fillHeight) {
|
||||||
|
if (item != null) {
|
||||||
imageUrlService.getItemImageUrl(
|
imageUrlService.getItemImageUrl(
|
||||||
item,
|
item,
|
||||||
ImageType.PRIMARY,
|
ImageType.PRIMARY,
|
||||||
|
|
@ -101,7 +103,7 @@ fun BannerCard(
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
// .background(MaterialTheme.colorScheme.surfaceVariant),
|
// .background(MaterialTheme.colorScheme.surfaceVariant),
|
||||||
) {
|
) {
|
||||||
if (!imageError && imageUrl.isNotNullOrBlank()) {
|
if (!imageError && imageUrl != null) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = imageUrl,
|
model = imageUrl,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -121,7 +123,7 @@ fun BannerCard(
|
||||||
.align(Alignment.Center),
|
.align(Alignment.Center),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (played || cornerText.isNotNullOrBlank()) {
|
if (played || cornerText != null) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|
@ -133,7 +135,7 @@ fun BannerCard(
|
||||||
if (played && (playPercent <= 0 || playPercent >= 100)) {
|
if (played && (playPercent <= 0 || playPercent >= 100)) {
|
||||||
WatchedIcon(Modifier.size(24.dp))
|
WatchedIcon(Modifier.size(24.dp))
|
||||||
}
|
}
|
||||||
if (cornerText.isNotNullOrBlank()) {
|
if (cornerText != null) {
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.SeriesName
|
import com.github.damontecres.wholphin.ui.components.SeriesName
|
||||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||||
import com.github.damontecres.wholphin.ui.formatDateTime
|
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.logTab
|
import com.github.damontecres.wholphin.ui.logTab
|
||||||
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
||||||
|
|
@ -215,9 +214,6 @@ fun SeriesOverviewContent(
|
||||||
if (interactionSource.collectIsFocusedAsState().value) {
|
if (interactionSource.collectIsFocusedAsState().value) {
|
||||||
onFocusEpisode.invoke(episodeIndex)
|
onFocusEpisode.invoke(episodeIndex)
|
||||||
}
|
}
|
||||||
val cornerText =
|
|
||||||
episode?.data?.indexNumber?.let { "E$it" }
|
|
||||||
?: episode?.data?.premiereDate?.let(::formatDateTime)
|
|
||||||
BannerCard(
|
BannerCard(
|
||||||
name = episode?.name,
|
name = episode?.name,
|
||||||
item = episode,
|
item = episode,
|
||||||
|
|
@ -226,7 +222,7 @@ fun SeriesOverviewContent(
|
||||||
?.aspectRatio
|
?.aspectRatio
|
||||||
?.coerceAtLeast(AspectRatios.FOUR_THREE)
|
?.coerceAtLeast(AspectRatios.FOUR_THREE)
|
||||||
?: (AspectRatios.WIDE),
|
?: (AspectRatios.WIDE),
|
||||||
cornerText = cornerText,
|
cornerText = episode?.ui?.episodeCornerText,
|
||||||
played = episode?.data?.userData?.played ?: false,
|
played = episode?.data?.userData?.played ?: false,
|
||||||
playPercent =
|
playPercent =
|
||||||
episode?.data?.userData?.playedPercentage
|
episode?.data?.userData?.playedPercentage
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.github.damontecres.wholphin.ui.main
|
package com.github.damontecres.wholphin.ui.main
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.focusGroup
|
import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
|
|
@ -25,7 +24,6 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -49,7 +47,6 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.Cards
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
import com.github.damontecres.wholphin.ui.abbreviateNumber
|
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
|
|
@ -88,28 +85,15 @@ fun HomePage(
|
||||||
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var firstLoad by rememberSaveable { mutableStateOf(true) }
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.init(preferences).join()
|
viewModel.init()
|
||||||
firstLoad = false
|
|
||||||
}
|
}
|
||||||
val loading by viewModel.loadingState.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loadingState.observeAsState(LoadingState.Loading)
|
||||||
val refreshing by viewModel.refreshState.observeAsState(LoadingState.Loading)
|
val refreshing by viewModel.refreshState.observeAsState(LoadingState.Loading)
|
||||||
val watchingRows by viewModel.watchingRows.observeAsState(listOf())
|
val watchingRows by viewModel.watchingRows.observeAsState(listOf())
|
||||||
val latestRows by viewModel.latestRows.observeAsState(listOf())
|
val latestRows by viewModel.latestRows.observeAsState(listOf())
|
||||||
LaunchedEffect(loading) {
|
|
||||||
val state = loading
|
val homeRows = remember(watchingRows, latestRows) { watchingRows + latestRows }
|
||||||
if (!firstLoad && state is LoadingState.Error) {
|
|
||||||
// After the first load, refreshes occur in the background and an ErrorMessage won't show
|
|
||||||
// So send a Toast on errors instead
|
|
||||||
Toast
|
|
||||||
.makeText(
|
|
||||||
context,
|
|
||||||
"Home refresh error: ${state.localizedMessage}",
|
|
||||||
Toast.LENGTH_LONG,
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> {
|
is LoadingState.Error -> {
|
||||||
|
|
@ -127,7 +111,7 @@ fun HomePage(
|
||||||
var showPlaylistDialog by remember { mutableStateOf<UUID?>(null) }
|
var showPlaylistDialog by remember { mutableStateOf<UUID?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
HomePageContent(
|
HomePageContent(
|
||||||
watchingRows + latestRows,
|
homeRows = homeRows,
|
||||||
onClickItem = { position, item ->
|
onClickItem = { position, item ->
|
||||||
viewModel.navigationManager.navigateTo(item.destination())
|
viewModel.navigationManager.navigateTo(item.destination())
|
||||||
},
|
},
|
||||||
|
|
@ -339,21 +323,11 @@ fun HomePageContent(
|
||||||
.focusRequester(rowFocusRequesters[rowIndex])
|
.focusRequester(rowFocusRequesters[rowIndex])
|
||||||
.animateItem(),
|
.animateItem(),
|
||||||
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
||||||
val cornerText =
|
|
||||||
remember(item) {
|
|
||||||
item?.data?.indexNumber?.let { "E$it" }
|
|
||||||
?: item
|
|
||||||
?.data
|
|
||||||
?.userData
|
|
||||||
?.unplayedItemCount
|
|
||||||
?.takeIf { it > 0 }
|
|
||||||
?.let { abbreviateNumber(it) }
|
|
||||||
}
|
|
||||||
BannerCard(
|
BannerCard(
|
||||||
name = item?.data?.seriesName ?: item?.name,
|
name = item?.data?.seriesName ?: item?.name,
|
||||||
item = item,
|
item = item,
|
||||||
aspectRatio = AspectRatios.TALL,
|
aspectRatio = AspectRatios.TALL,
|
||||||
cornerText = cornerText,
|
cornerText = item?.ui?.episdodeUnplayedCornerText,
|
||||||
played = item?.data?.userData?.played ?: false,
|
played = item?.data?.userData?.played ?: false,
|
||||||
favorite = item?.favorite ?: false,
|
favorite = item?.favorite ?: false,
|
||||||
playPercent =
|
playPercent =
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,11 @@ import com.github.damontecres.wholphin.services.DatePlayedService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.LatestNextUpService
|
import com.github.damontecres.wholphin.services.LatestNextUpService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
|
|
@ -24,7 +26,6 @@ import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
|
@ -47,6 +48,7 @@ class HomeViewModel
|
||||||
private val datePlayedService: DatePlayedService,
|
private val datePlayedService: DatePlayedService,
|
||||||
private val latestNextUpService: LatestNextUpService,
|
private val latestNextUpService: LatestNextUpService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
|
private val userPreferencesService: UserPreferencesService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val loadingState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
val loadingState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||||
val refreshState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
val refreshState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||||
|
|
@ -57,18 +59,11 @@ class HomeViewModel
|
||||||
|
|
||||||
init {
|
init {
|
||||||
datePlayedService.invalidateAll()
|
datePlayedService.invalidateAll()
|
||||||
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(preferences: UserPreferences): Job {
|
fun init() {
|
||||||
val reload = loadingState.value != LoadingState.Success
|
viewModelScope.launch(
|
||||||
if (reload) {
|
|
||||||
loadingState.value = LoadingState.Loading
|
|
||||||
}
|
|
||||||
refreshState.value = LoadingState.Loading
|
|
||||||
this.preferences = preferences
|
|
||||||
val prefs = preferences.appPreferences.homePagePreferences
|
|
||||||
val limit = prefs.maxItemsPerRow
|
|
||||||
return viewModelScope.launch(
|
|
||||||
Dispatchers.IO +
|
Dispatchers.IO +
|
||||||
LoadingExceptionHandler(
|
LoadingExceptionHandler(
|
||||||
loadingState,
|
loadingState,
|
||||||
|
|
@ -76,10 +71,18 @@ class HomeViewModel
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Timber.d("init HomeViewModel")
|
Timber.d("init HomeViewModel")
|
||||||
|
val reload = loadingState.value != LoadingState.Success
|
||||||
|
if (reload) {
|
||||||
|
loadingState.setValueOnMain(LoadingState.Loading)
|
||||||
|
}
|
||||||
|
refreshState.setValueOnMain(LoadingState.Loading)
|
||||||
|
this@HomeViewModel.preferences = userPreferencesService.getCurrent()
|
||||||
|
val prefs = preferences.appPreferences.homePagePreferences
|
||||||
|
val limit = prefs.maxItemsPerRow
|
||||||
if (reload) {
|
if (reload) {
|
||||||
backdropService.clearBackdrop()
|
backdropService.clearBackdrop()
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
serverRepository.currentUserDto.value?.let { userDto ->
|
serverRepository.currentUserDto.value?.let { userDto ->
|
||||||
val includedIds =
|
val includedIds =
|
||||||
navDrawerItemRepository
|
navDrawerItemRepository
|
||||||
|
|
@ -138,6 +141,14 @@ class HomeViewModel
|
||||||
val loadedLatest = latestNextUpService.loadLatest(latest)
|
val loadedLatest = latestNextUpService.loadLatest(latest)
|
||||||
this@HomeViewModel.latestRows.setValueOnMain(loadedLatest)
|
this@HomeViewModel.latestRows.setValueOnMain(loadedLatest)
|
||||||
}
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
if (!reload) {
|
||||||
|
loadingState.setValueOnMain(LoadingState.Error(ex))
|
||||||
|
} else {
|
||||||
|
showToast(context, "Error refreshing home: ${ex.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +158,7 @@ class HomeViewModel
|
||||||
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
favoriteWatchManager.setWatched(itemId, played)
|
favoriteWatchManager.setWatched(itemId, played)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
init(preferences)
|
init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +168,7 @@ class HomeViewModel
|
||||||
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
favoriteWatchManager.setFavorite(itemId, favorite)
|
favoriteWatchManager.setFavorite(itemId, favorite)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
init(preferences)
|
init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package com.github.damontecres.wholphin.ui.nav
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
|
||||||
import androidx.compose.foundation.focusGroup
|
import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
|
|
@ -14,7 +13,6 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
|
@ -26,13 +24,10 @@ import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
|
@ -94,7 +89,6 @@ import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -117,9 +111,9 @@ class NavDrawerViewModel
|
||||||
val backdropService: BackdropService,
|
val backdropService: BackdropService,
|
||||||
private val seerrServerRepository: SeerrServerRepository,
|
private val seerrServerRepository: SeerrServerRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
// private var all: List<NavDrawerItem>? = null
|
|
||||||
val moreLibraries = MutableLiveData<List<NavDrawerItem>>(null)
|
val moreLibraries = MutableLiveData<List<NavDrawerItem>>(null)
|
||||||
val libraries = MutableLiveData<List<NavDrawerItem>>(listOf())
|
val libraries = MutableLiveData<List<NavDrawerItem>>(listOf())
|
||||||
|
|
||||||
val selectedIndex = MutableLiveData(-1)
|
val selectedIndex = MutableLiveData(-1)
|
||||||
val showMore = MutableLiveData(false)
|
val showMore = MutableLiveData(false)
|
||||||
|
|
||||||
|
|
@ -133,7 +127,6 @@ class NavDrawerViewModel
|
||||||
fun init() {
|
fun init() {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
val all = navDrawerItemRepository.getNavDrawerItems()
|
val all = navDrawerItemRepository.getNavDrawerItems()
|
||||||
// this@NavDrawerViewModel.all = all
|
|
||||||
val libraries = navDrawerItemRepository.getFilteredNavDrawerItems(all)
|
val libraries = navDrawerItemRepository.getFilteredNavDrawerItems(all)
|
||||||
val moreLibraries = all.toMutableList().apply { removeAll(libraries) }
|
val moreLibraries = all.toMutableList().apply { removeAll(libraries) }
|
||||||
|
|
||||||
|
|
@ -187,6 +180,37 @@ class NavDrawerViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onClickDrawerItem(
|
||||||
|
index: Int,
|
||||||
|
item: NavDrawerItem,
|
||||||
|
) {
|
||||||
|
if (item !is NavDrawerItem.More) setShowMore(false)
|
||||||
|
when (item) {
|
||||||
|
NavDrawerItem.Favorites -> {
|
||||||
|
setIndex(index)
|
||||||
|
navigationManager.navigateToFromDrawer(
|
||||||
|
Destination.Favorites,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavDrawerItem.More -> {
|
||||||
|
setShowMore(!showMore.value!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavDrawerItem.Discover -> {
|
||||||
|
setIndex(index)
|
||||||
|
navigationManager.navigateToFromDrawer(
|
||||||
|
Destination.Discover,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ServerNavDrawerItem -> {
|
||||||
|
setIndex(index)
|
||||||
|
navigationManager.navigateToFromDrawer(item.destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setIndex(index: Int) {
|
fun setIndex(index: Int) {
|
||||||
selectedIndex.value = index
|
selectedIndex.value = index
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +274,7 @@ fun NavDrawer(
|
||||||
viewModel: NavDrawerViewModel =
|
viewModel: NavDrawerViewModel =
|
||||||
hiltViewModel(
|
hiltViewModel(
|
||||||
LocalView.current.findViewTreeViewModelStoreOwner()!!,
|
LocalView.current.findViewTreeViewModelStoreOwner()!!,
|
||||||
key = "${server?.id}_${user?.id}", // Keyed to the server & user to ensure its reset when switching either
|
key = "${server.id}_${user.id}", // Keyed to the server & user to ensure its reset when switching either
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
|
|
@ -271,76 +295,14 @@ fun NavDrawer(
|
||||||
LaunchedEffect(Unit) { viewModel.init() }
|
LaunchedEffect(Unit) { viewModel.init() }
|
||||||
|
|
||||||
val showMore by viewModel.showMore.observeAsState(false)
|
val showMore by viewModel.showMore.observeAsState(false)
|
||||||
// val libraries = if (showPinnedOnly) pinnedLibraries else allLibraries
|
|
||||||
// A negative index is a built in page, >=0 is a library
|
// A negative index is a built in page, >=0 is a library
|
||||||
val selectedIndex by viewModel.selectedIndex.observeAsState(-1)
|
val selectedIndex by viewModel.selectedIndex.observeAsState(-1)
|
||||||
var focusedIndex by remember { mutableIntStateOf(Int.MIN_VALUE) }
|
|
||||||
val derivedFocusedIndex by remember { derivedStateOf { focusedIndex } }
|
|
||||||
|
|
||||||
fun setShowMore(value: Boolean) {
|
|
||||||
viewModel.setShowMore(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
BackHandler(enabled = showMore && drawerState.currentValue == DrawerValue.Open) {
|
BackHandler(enabled = showMore && drawerState.currentValue == DrawerValue.Open) {
|
||||||
setShowMore(false)
|
viewModel.setShowMore(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val onClick = { index: Int, item: NavDrawerItem ->
|
val closedDrawerWidth = NavigationDrawerItemDefaults.CollapsedDrawerItemWidth
|
||||||
when (item) {
|
|
||||||
NavDrawerItem.Favorites -> {
|
|
||||||
viewModel.setIndex(index)
|
|
||||||
viewModel.navigationManager.navigateToFromDrawer(
|
|
||||||
Destination.Favorites,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavDrawerItem.More -> {
|
|
||||||
setShowMore(!showMore)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavDrawerItem.Discover -> {
|
|
||||||
viewModel.setIndex(index)
|
|
||||||
viewModel.navigationManager.navigateToFromDrawer(
|
|
||||||
Destination.Discover,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ServerNavDrawerItem -> {
|
|
||||||
viewModel.setIndex(index)
|
|
||||||
viewModel.navigationManager.navigateToFromDrawer(item.destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Temporarily disabled, see https://github.com/damontecres/Wholphin/pull/127#issuecomment-3478058418
|
|
||||||
if (false && preferences.appPreferences.interfacePreferences.navDrawerSwitchOnFocus) {
|
|
||||||
LaunchedEffect(derivedFocusedIndex) {
|
|
||||||
val index = derivedFocusedIndex
|
|
||||||
delay(600)
|
|
||||||
if (index != selectedIndex) {
|
|
||||||
if (index == -1) {
|
|
||||||
viewModel.setIndex(-1)
|
|
||||||
viewModel.navigationManager.goToHome()
|
|
||||||
} else if (index in libraries.indices) {
|
|
||||||
if (moreLibraries.isEmpty() || index != libraries.lastIndex) {
|
|
||||||
libraries.getOrNull(index)?.let {
|
|
||||||
onClick.invoke(index, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val newIndex = libraries.size - index + 1
|
|
||||||
if (newIndex in moreLibraries.indices) {
|
|
||||||
moreLibraries.getOrNull(newIndex)?.let {
|
|
||||||
onClick.invoke(index, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val closedDrawerWidth = 40.dp
|
|
||||||
val drawerWidth by animateDpAsState(if (drawerState.isOpen) 260.dp else closedDrawerWidth)
|
|
||||||
val drawerPadding by animateDpAsState(if (drawerState.isOpen) 0.dp else 8.dp)
|
|
||||||
val drawerBackground by animateColorAsState(
|
val drawerBackground by animateColorAsState(
|
||||||
if (drawerState.isOpen) {
|
if (drawerState.isOpen) {
|
||||||
MaterialTheme.colorScheme.surface
|
MaterialTheme.colorScheme.surface
|
||||||
|
|
@ -382,15 +344,12 @@ fun NavDrawer(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.width(drawerWidth)
|
|
||||||
.drawBehind {
|
.drawBehind {
|
||||||
drawRect(drawerBackground)
|
drawRect(drawerBackground)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
// Even though some must be clicked, focusing on it should clear other focused items
|
// Even though some must be clicked, focusing on it should clear other focused items
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE }
|
|
||||||
val userImageUrl = remember(user) { viewModel.getUserImage(user) }
|
val userImageUrl = remember(user) { viewModel.getUserImage(user) }
|
||||||
ProfileIcon(
|
ProfileIcon(
|
||||||
user = user,
|
user = user,
|
||||||
|
|
@ -403,7 +362,7 @@ fun NavDrawer(
|
||||||
SetupDestination.UserList(server),
|
SetupDestination.UserList(server),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(start = drawerPadding),
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
|
|
@ -426,13 +385,10 @@ fun NavDrawer(
|
||||||
scrollToSelected()
|
scrollToSelected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.fillMaxHeight()
|
}.fillMaxHeight(),
|
||||||
.padding(start = drawerPadding),
|
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) { if (focused) focusedIndex = -2 }
|
|
||||||
IconNavItem(
|
IconNavItem(
|
||||||
text = stringResource(R.string.search),
|
text = stringResource(R.string.search),
|
||||||
icon = Icons.Default.Search,
|
icon = Icons.Default.Search,
|
||||||
|
|
@ -449,13 +405,11 @@ fun NavDrawer(
|
||||||
.ifElse(
|
.ifElse(
|
||||||
selectedIndex == -2,
|
selectedIndex == -2,
|
||||||
Modifier.focusRequester(focusRequester),
|
Modifier.focusRequester(focusRequester),
|
||||||
).animateItem(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) { if (focused) focusedIndex = -1 }
|
|
||||||
IconNavItem(
|
IconNavItem(
|
||||||
text = stringResource(R.string.home),
|
text = stringResource(R.string.home),
|
||||||
icon = Icons.Default.Home,
|
icon = Icons.Default.Home,
|
||||||
|
|
@ -475,13 +429,11 @@ fun NavDrawer(
|
||||||
.ifElse(
|
.ifElse(
|
||||||
selectedIndex == -1,
|
selectedIndex == -1,
|
||||||
Modifier.focusRequester(focusRequester),
|
Modifier.focusRequester(focusRequester),
|
||||||
).animateItem(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
itemsIndexed(libraries) { index, it ->
|
itemsIndexed(libraries) { index, it ->
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) { if (focused) focusedIndex = index }
|
|
||||||
NavItem(
|
NavItem(
|
||||||
library = it,
|
library = it,
|
||||||
selected = selectedIndex == index,
|
selected = selectedIndex == index,
|
||||||
|
|
@ -489,31 +441,26 @@ fun NavDrawer(
|
||||||
drawerOpen = drawerState.isOpen,
|
drawerOpen = drawerState.isOpen,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick.invoke(index, it)
|
viewModel.onClickDrawerItem(index, it)
|
||||||
if (it !is NavDrawerItem.More) setShowMore(false)
|
|
||||||
},
|
},
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.ifElse(
|
.ifElse(
|
||||||
selectedIndex == index,
|
selectedIndex == index,
|
||||||
Modifier.focusRequester(focusRequester),
|
Modifier.focusRequester(focusRequester),
|
||||||
).animateItem(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (showMore) {
|
if (showMore) {
|
||||||
itemsIndexed(moreLibraries) { index, it ->
|
itemsIndexed(moreLibraries) { index, it ->
|
||||||
val adjustedIndex = (index + libraries.size + 1)
|
val adjustedIndex = (index + libraries.size + 1)
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) {
|
|
||||||
if (focused) focusedIndex = adjustedIndex
|
|
||||||
}
|
|
||||||
NavItem(
|
NavItem(
|
||||||
library = it,
|
library = it,
|
||||||
selected = selectedIndex == adjustedIndex,
|
selected = selectedIndex == adjustedIndex,
|
||||||
moreExpanded = showMore,
|
moreExpanded = showMore,
|
||||||
drawerOpen = drawerState.isOpen,
|
drawerOpen = drawerState.isOpen,
|
||||||
onClick = { onClick.invoke(adjustedIndex, it) },
|
onClick = { viewModel.onClickDrawerItem(adjustedIndex, it) },
|
||||||
containerColor =
|
containerColor =
|
||||||
if (drawerState.isOpen) {
|
if (drawerState.isOpen) {
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||||
|
|
@ -526,14 +473,12 @@ fun NavDrawer(
|
||||||
.ifElse(
|
.ifElse(
|
||||||
selectedIndex == adjustedIndex,
|
selectedIndex == adjustedIndex,
|
||||||
Modifier.focusRequester(focusRequester),
|
Modifier.focusRequester(focusRequester),
|
||||||
).animateItem(),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE }
|
|
||||||
IconNavItem(
|
IconNavItem(
|
||||||
text = stringResource(R.string.settings),
|
text = stringResource(R.string.settings),
|
||||||
icon = Icons.Default.Settings,
|
icon = Icons.Default.Settings,
|
||||||
|
|
@ -547,7 +492,7 @@ fun NavDrawer(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -587,7 +532,6 @@ fun NavigationDrawerScope.ProfileIcon(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
) {
|
) {
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
selected = false,
|
selected = false,
|
||||||
|
|
@ -639,7 +583,7 @@ fun NavigationDrawerScope.IconNavItem(
|
||||||
icon,
|
icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = color,
|
tint = color,
|
||||||
modifier = Modifier.padding(0.dp),
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
supportingContent =
|
supportingContent =
|
||||||
|
|
@ -675,6 +619,7 @@ fun NavigationDrawerScope.NavItem(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val useFont = library !is ServerNavDrawerItem || library.type != CollectionType.LIVETV
|
val useFont = library !is ServerNavDrawerItem || library.type != CollectionType.LIVETV
|
||||||
val icon =
|
val icon =
|
||||||
|
remember(library) {
|
||||||
when (library) {
|
when (library) {
|
||||||
NavDrawerItem.Favorites -> {
|
NavDrawerItem.Favorites -> {
|
||||||
R.string.fa_heart
|
R.string.fa_heart
|
||||||
|
|
@ -701,6 +646,7 @@ fun NavigationDrawerScope.NavItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -732,13 +678,16 @@ fun NavigationDrawerScope.NavItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent =
|
||||||
if (library is NavDrawerItem.More) {
|
if (library is NavDrawerItem.More) {
|
||||||
|
{
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (moreExpanded) Icons.Default.ArrowDropDown else Icons.Default.KeyboardArrowLeft,
|
imageVector = if (moreExpanded) Icons.Default.ArrowDropDown else Icons.Default.KeyboardArrowLeft,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
},
|
},
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import com.github.damontecres.wholphin.ui.TimeFormatter
|
import com.github.damontecres.wholphin.ui.TimeFormatter
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
val LocalClock = compositionLocalOf<Clock> { Clock() }
|
val LocalClock = compositionLocalOf<Clock> { Clock() }
|
||||||
|
|
@ -32,6 +34,7 @@ data class Clock(
|
||||||
fun ProvideLocalClock(content: @Composable () -> Unit) {
|
fun ProvideLocalClock(content: @Composable () -> Unit) {
|
||||||
val clock = remember { Clock() }
|
val clock = remember { Clock() }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val now = LocalDateTime.now()
|
val now = LocalDateTime.now()
|
||||||
val time = TimeFormatter.format(now)
|
val time = TimeFormatter.format(now)
|
||||||
|
|
@ -40,5 +43,6 @@ fun ProvideLocalClock(content: @Composable () -> Unit) {
|
||||||
delay(2_000)
|
delay(2_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
CompositionLocalProvider(LocalClock provides clock, content)
|
CompositionLocalProvider(LocalClock provides clock, content)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue