Add "Ends at" time display. (#475)

Addresses #441 .

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
This commit is contained in:
Amar Sandhu 2025-12-18 13:52:27 -07:00 committed by GitHub
parent 1855c4666b
commit fd260f2709
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 56 additions and 24 deletions

View file

@ -1,29 +1,32 @@
package com.github.damontecres.wholphin.ui.components
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.tv.material3.MaterialTheme
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.TimeFormatter
import com.github.damontecres.wholphin.ui.roundMinutes
import com.github.damontecres.wholphin.ui.timeRemaining
import com.github.damontecres.wholphin.ui.util.LocalClock
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.extensions.ticks
import java.time.LocalDateTime
@Composable
fun MovieQuickDetails(
dto: BaseItemDto?,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val now = LocalClock.current.now
val details =
remember(dto) {
remember(dto, now) {
buildList {
dto?.productionYear?.let { add(it.toString()) }
dto?.runTimeTicks?.ticks?.roundMinutes?.let {
add(it.toString())
}
dto?.timeRemaining?.roundMinutes?.let {
add("$it left")
}
addRuntimeDetails(context, now, dto)
dto?.officialRating?.let(::add)
}
}
@ -36,3 +39,21 @@ fun MovieQuickDetails(
modifier = modifier,
)
}
fun MutableList<String>.addRuntimeDetails(
context: Context,
now: LocalDateTime,
dto: BaseItemDto?,
) {
val runtime = dto?.runTimeTicks?.ticks
runtime?.let { duration ->
add(duration.roundMinutes.toString())
}
dto?.timeRemaining?.roundMinutes?.let {
add("$it left")
}
(dto?.timeRemaining ?: runtime)?.let { remaining ->
val endTimeStr = TimeFormatter.format(now.plusSeconds(remaining.inWholeSeconds))
add(context.getString(R.string.ends_at, endTimeStr))
}
}

View file

@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.tv.material3.MaterialTheme
@ -11,7 +12,7 @@ import com.github.damontecres.wholphin.ui.formatDateTime
import com.github.damontecres.wholphin.ui.roundMinutes
import com.github.damontecres.wholphin.ui.seasonEpisode
import com.github.damontecres.wholphin.ui.seriesProductionYears
import com.github.damontecres.wholphin.ui.timeRemaining
import com.github.damontecres.wholphin.ui.util.LocalClock
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.extensions.ticks
@ -57,17 +58,14 @@ fun EpisodeQuickDetails(
dto: BaseItemDto?,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val now = LocalClock.current.now
val details =
remember(dto) {
remember(dto, now) {
buildList {
dto?.seasonEpisode?.let(::add)
dto?.premiereDate?.let { add(formatDateTime(it)) }
val duration = dto?.runTimeTicks?.ticks
duration
?.roundMinutes
?.toString()
?.let(::add)
dto?.timeRemaining?.roundMinutes?.let { add("$it left") }
addRuntimeDetails(context, now, dto)
dto?.officialRating?.let(::add)
}
}
@ -89,11 +87,9 @@ fun SeriesQuickDetails(
remember(dto) {
buildList {
dto?.seriesProductionYears?.let(::add)
val duration = dto?.runTimeTicks?.ticks
duration
?.roundMinutes
?.toString()
?.let(::add)
dto?.runTimeTicks?.ticks?.roundMinutes?.let {
add(it.toString())
}
dto?.officialRating?.let(::add)
}
}

View file

@ -53,6 +53,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.services.BackdropService
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.ui.DefaultItemFields
import com.github.damontecres.wholphin.ui.TimeFormatter
import com.github.damontecres.wholphin.ui.cards.ItemCardImage
import com.github.damontecres.wholphin.ui.components.DialogItem
import com.github.damontecres.wholphin.ui.components.DialogParams
@ -68,6 +69,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.roundMinutes
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalClock
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.GetPlaylistItemsRequestHandler
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
@ -407,10 +409,22 @@ fun PlaylistItem(
)
},
trailingContent = {
item?.data?.runTimeTicks?.ticks?.roundMinutes?.let {
Text(
text = it.toString(),
)
item?.data?.runTimeTicks?.ticks?.roundMinutes?.let { duration ->
val now = LocalClock.current.now
val endTimeStr =
remember(item, now) {
val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds)
TimeFormatter.format(endTime)
}
Column {
Text(
text = duration.toString(),
)
Text(
text = stringResource(R.string.ends_at, endTimeStr),
style = MaterialTheme.typography.bodySmall,
)
}
}
},
leadingContent = {

View file

@ -36,6 +36,7 @@
<string name="download_and_update"><![CDATA[Download & Update]]></string>
<string name="downloading">Downloading…</string>
<string name="enabled">Enabled</string>
<string name="ends_at">Ends at %1$s</string>
<string name="enter_server_url">Enter Server IP or URL</string>
<string name="enter_server_address">Enter server address</string>
<string name="episodes">Episodes</string>