TV Guide UI followup to #517 (#521)

## Description
- Instead of just replacing newline characters, collapse white-space
into single space characters which is roughly how the web UI does it
with CSS's `white-space: nowrap;`
- Use an actual arrow indicator instead of a `<` character for when a
program begins before the guide start

### Related issues
#517
https://github.com/damontecres/Wholphin/issues/434
https://github.com/damontecres/Wholphin/issues/435
https://github.com/damontecres/Wholphin/issues/436

### Screenshots
<img width="605" height="316" alt="image"
src="https://github.com/user-attachments/assets/6a5227b5-9960-4c6d-a086-a4a9867ea60b"
/>
This commit is contained in:
Ray 2025-12-20 14:47:28 -05:00 committed by GitHub
parent a65b93e064
commit 11dda19b8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 38 deletions

View file

@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -21,6 +22,8 @@ import androidx.tv.material3.Text
import androidx.tv.material3.contentColorFor import androidx.tv.material3.contentColorFor
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.FontAwesome
import java.time.LocalDateTime import java.time.LocalDateTime
@Composable @Composable
@ -56,14 +59,7 @@ fun Program(
RoundedCornerShape(cornerSize) RoundedCornerShape(cornerSize)
} }
} }
val title = val title = program.name ?: program.id.toString()
remember(startedBeforeGuide) {
if (startedBeforeGuide) {
"< "
} else {
""
} + (program.name ?: program.id.toString())
}
Box( Box(
modifier = modifier =
modifier modifier
@ -74,12 +70,27 @@ fun Program(
shape = shape, shape = shape,
), ),
) { ) {
Row(
modifier = Modifier.fillMaxSize(),
) {
if (startedBeforeGuide) {
Text(
text = stringResource(R.string.fa_caret_left),
fontFamily = FontAwesome,
color = textColor,
fontSize = 16.sp,
modifier =
Modifier
.align(Alignment.CenterVertically)
.padding(start = 2.dp),
)
}
Column( Column(
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = modifier =
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.padding(4.dp), .padding(start = 2.dp, end = 4.dp, top = 4.dp, bottom = 4.dp),
) { ) {
Text( Text(
text = title, text = title,
@ -104,6 +115,7 @@ fun Program(
) )
} }
} }
}
RecordingMarker( RecordingMarker(
isRecording = program.isRecording, isRecording = program.isRecording,
isSeriesRecording = program.isSeriesRecording, isSeriesRecording = program.isSeriesRecording,

View file

@ -229,11 +229,12 @@ class LiveTvViewModel
} else { } else {
null null
} }
val name = (dto.seriesName ?: dto.name)?.replace(Regex("[\n\r]"), "") // Clean up name/subtitles by collapsing whitespace (including newlines) into single spaces
val name = (dto.seriesName ?: dto.name)?.replace(Regex("\\s+"), " ")
val subtitle = val subtitle =
dto.episodeTitle dto.episodeTitle
.takeIf { dto.isSeries ?: false } .takeIf { dto.isSeries ?: false }
?.replace(Regex("[\n\r]"), "") ?.replace(Regex("\\s+"), " ")
val p = val p =
TvProgram( TvProgram(
id = dto.id, id = dto.id,

View file

@ -14,6 +14,8 @@
<string name="fa_magnifying_glass_minus" translatable="false">&#xf010;</string> <string name="fa_magnifying_glass_minus" translatable="false">&#xf010;</string>
<string name="fa_caret_up" translatable="false">&#xf0d8;</string> <string name="fa_caret_up" translatable="false">&#xf0d8;</string>
<string name="fa_caret_down" translatable="false">&#xf0d7;</string> <string name="fa_caret_down" translatable="false">&#xf0d7;</string>
<string name="fa_caret_left" translatable="false">&#xf0d9;</string>
<string name="fa_caret_right" translatable="false">&#xf0da;</string>
<string name="fa_heart" translatable="false">&#xf004;</string> <string name="fa_heart" translatable="false">&#xf004;</string>
<string name="fa_video" translatable="false">&#xf03d;</string> <string name="fa_video" translatable="false">&#xf03d;</string>
<string name="fa_play" translatable="false">&#xf04b;</string> <string name="fa_play" translatable="false">&#xf04b;</string>