vc=65: metadata consistency across ChannelScreen + SearchScreen rows

vc=64 fixed RelatedRow's empty-metadata bug. ChannelVideoRow and
ResultRow had the same shape problem AND duplicated duration:

- ChannelVideoRow: showed 'N views · 0:42' which doubled with
  the VideoThumbnail's bottom-right duration badge. Stripped
  the duration text, added uploadDateRelative. Now reads
  'N views · 2 days ago' matching YT's channel page format.
- ResultRow: same duplicate-duration. Same fix. Search results
  now show 'N views · 2 days ago' under the uploader line.

All four video-row composables (FeedRow, RelatedRow,
ChannelVideoRow, ResultRow) now use the same leading-separator
buildString pattern: 'piece [· piece]*' that gracefully composes
whatever fields are populated. No more empty metadata lines, no
more duplicate duration.
This commit is contained in:
Sulkta 2026-05-26 13:09:23 -07:00
parent c007a1567e
commit 767e46abb6
3 changed files with 36 additions and 20 deletions

View file

@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app"
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via // vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no // strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
// NewPipeExtractor in the runtime path. // NewPipeExtractor in the runtime path.
const val STRAW_VERSION_CODE = 64 const val STRAW_VERSION_CODE = 65
const val STRAW_VERSION_NAME = "0.1.0-BX" const val STRAW_VERSION_NAME = "0.1.0-BY"
const val STRAW_APPLICATION_ID = "com.sulkta.straw" const val STRAW_APPLICATION_ID = "com.sulkta.straw"

View file

@ -205,18 +205,26 @@ private fun ChannelVideoRow(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
Text( // Don't repeat duration here — VideoThumbnail's
text = buildString { // bottom-right badge already shows it. Add the upload
if (item.viewCount > 0) append("${formatCount(item.viewCount)} views") // date so the row reads 'N views · 2 days ago' the way
if (item.durationSeconds > 0) { // YT renders it. vc=65 — a tester caught the duplicate
if (isNotEmpty()) append(" · ") // duration + missing date on the channel page.
append(formatDuration(item.durationSeconds)) val meta = buildString {
} if (item.viewCount > 0) append("${formatCount(item.viewCount)} views")
}, if (item.uploadDateRelative.isNotBlank()) {
style = MaterialTheme.typography.bodySmall, if (isNotEmpty()) append(" · ")
color = MaterialTheme.colorScheme.onSurfaceVariant, append(item.uploadDateRelative)
maxLines = 1, }
) }
if (meta.isNotEmpty()) {
Text(
text = meta,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
)
}
} }
} }
} }

View file

@ -247,13 +247,21 @@ private fun ResultRow(
else else
Modifier.padding(vertical = 4.dp), Modifier.padding(vertical = 4.dp),
) )
if (item.viewCount > 0 || item.durationSeconds > 0) { // Drop the duration here — VideoThumbnail's badge already
// renders it on the bottom-right of the thumbnail. Add the
// upload date instead so search results read like YT's
// own format. vc=65 — caught with the
// channel-page + related-row consistency pass.
val meta = buildString {
if (item.viewCount > 0) append(formatViews(item.viewCount))
if (item.uploadDateRelative.isNotBlank()) {
if (isNotEmpty()) append(" · ")
append(item.uploadDateRelative)
}
}
if (meta.isNotEmpty()) {
Text( Text(
text = buildString { text = meta,
if (item.viewCount > 0) append(formatViews(item.viewCount))
if (item.viewCount > 0 && item.durationSeconds > 0) append(" · ")
if (item.durationSeconds > 0) append(formatDuration(item.durationSeconds))
},
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1, maxLines = 1,