refactor(android): use shared formatDuration + ms/sec conversions
This commit is contained in:
@@ -11,6 +11,8 @@ import com.fabledsword.minstrel.models.wire.AlbumWire
|
||||
import com.fabledsword.minstrel.models.wire.ArtistDetailWire
|
||||
import com.fabledsword.minstrel.models.wire.ArtistWire
|
||||
import com.fabledsword.minstrel.models.wire.TrackWire
|
||||
import com.fabledsword.minstrel.shared.millisToSeconds
|
||||
import com.fabledsword.minstrel.shared.secondsToMillis
|
||||
|
||||
// Wire to Entity — used after a successful sync/fetch to persist into Room.
|
||||
// Entity fields are a subset of wire fields (display names + sort keys);
|
||||
@@ -37,7 +39,7 @@ fun TrackWire.toEntity(): CachedTrackEntity =
|
||||
albumId = albumId,
|
||||
artistId = artistId,
|
||||
title = title,
|
||||
durationMs = durationSec * MILLIS_PER_SECOND,
|
||||
durationMs = durationSec.secondsToMillis(),
|
||||
trackNumber = trackNumber,
|
||||
discNumber = discNumber,
|
||||
)
|
||||
@@ -94,7 +96,7 @@ fun CachedTrackEntity.toDomain(
|
||||
artistName = artistName,
|
||||
trackNumber = trackNumber,
|
||||
discNumber = discNumber,
|
||||
durationSec = durationMs / MILLIS_PER_SECOND,
|
||||
durationSec = durationMs.millisToSeconds(),
|
||||
)
|
||||
|
||||
// Wire to Domain — for fresh server responses that bypass the cache
|
||||
@@ -136,5 +138,3 @@ fun AlbumWire.toDomain(): AlbumRef =
|
||||
durationSec = durationSec,
|
||||
coverUrl = coverUrl,
|
||||
)
|
||||
|
||||
private const val MILLIS_PER_SECOND = 1000
|
||||
|
||||
@@ -55,6 +55,7 @@ import com.fabledsword.minstrel.models.AlbumRef
|
||||
import com.fabledsword.minstrel.models.TrackRef
|
||||
import com.fabledsword.minstrel.nav.AlbumDetail
|
||||
import com.fabledsword.minstrel.nav.ArtistDetail
|
||||
import com.fabledsword.minstrel.shared.formatDuration
|
||||
import com.fabledsword.minstrel.shared.widgets.CachedDot
|
||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||
import com.fabledsword.minstrel.shared.widgets.LikeButton
|
||||
@@ -352,7 +353,7 @@ private fun TrackRow(
|
||||
}
|
||||
CachedDot(track.id)
|
||||
Text(
|
||||
text = formatDuration(track.durationSec),
|
||||
text = formatDuration(track.durationSec, zero = "--:--"),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
@@ -421,12 +422,3 @@ private fun SeededAlbumLoading(seed: AlbumRef) {
|
||||
items(SKELETON_TRACK_ROWS) { SkeletonTrackRow() }
|
||||
}
|
||||
}
|
||||
|
||||
private const val SECONDS_PER_MINUTE = 60
|
||||
|
||||
private fun formatDuration(seconds: Int): String {
|
||||
if (seconds <= 0) return "--:--"
|
||||
val m = seconds / SECONDS_PER_MINUTE
|
||||
val s = seconds % SECONDS_PER_MINUTE
|
||||
return "$m:${s.toString().padStart(2, '0')}"
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ import com.fabledsword.minstrel.nav.LocalAnimatedContentScope
|
||||
import com.fabledsword.minstrel.nav.LocalSharedTransitionScope
|
||||
import com.fabledsword.minstrel.nav.NowPlaying
|
||||
import com.fabledsword.minstrel.nav.Queue
|
||||
import com.fabledsword.minstrel.shared.formatDuration
|
||||
import com.fabledsword.minstrel.shared.millisToSeconds
|
||||
import com.fabledsword.minstrel.shared.widgets.LikeButton
|
||||
import com.fabledsword.minstrel.shared.widgets.trackactions.TrackActionsButton
|
||||
import com.fabledsword.minstrel.shared.widgets.trackactions.TrackActionsViewModel
|
||||
@@ -78,8 +80,6 @@ import com.fabledsword.minstrel.theme.LocalActionColors
|
||||
private const val COVER_MAX_WIDTH_DP = 320
|
||||
private const val TRANSPORT_ICON_DP = 36
|
||||
private const val PLAY_PAUSE_ICON_DP = 56
|
||||
private const val MS_PER_SECOND = 1000L
|
||||
private const val SECONDS_PER_MINUTE = 60L
|
||||
private const val POP_GRACE_MS = 500L
|
||||
|
||||
// Vertical drag-down threshold (in pixels) past which the gesture
|
||||
@@ -420,12 +420,12 @@ private fun ScrubberRow(positionMs: Long, durationMs: Long, onSeek: (Long) -> Un
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = formatMillis(positionMs),
|
||||
text = formatDuration(positionMs.millisToSeconds()),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Text(
|
||||
text = formatMillis(durationMs),
|
||||
text = formatDuration(durationMs.millisToSeconds()),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
@@ -471,11 +471,3 @@ private fun TransportRow(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMillis(ms: Long): String {
|
||||
if (ms <= 0) return "0:00"
|
||||
val totalSeconds = ms / MS_PER_SECOND
|
||||
val minutes = totalSeconds / SECONDS_PER_MINUTE
|
||||
val seconds = totalSeconds % SECONDS_PER_MINUTE
|
||||
return "%d:%02d".format(minutes, seconds)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.composables.icons.lucide.ArrowLeft
|
||||
import com.composables.icons.lucide.Lucide
|
||||
import com.composables.icons.lucide.Volume2
|
||||
import com.fabledsword.minstrel.models.TrackRef
|
||||
import com.fabledsword.minstrel.shared.formatDuration
|
||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -149,12 +150,4 @@ private fun queueSubtitle(track: TrackRef): String = listOf(track.artistName, tr
|
||||
.filter { it.isNotEmpty() }
|
||||
.joinToString(" · ")
|
||||
|
||||
private const val SECONDS_PER_MINUTE = 60
|
||||
|
||||
private fun formatDuration(seconds: Int): String {
|
||||
val mins = seconds / SECONDS_PER_MINUTE
|
||||
val secs = seconds % SECONDS_PER_MINUTE
|
||||
return "%d:%02d".format(mins, secs)
|
||||
}
|
||||
|
||||
private const val HIGHLIGHT_ALPHA = 0.12f
|
||||
|
||||
+2
-9
@@ -72,6 +72,7 @@ import com.fabledsword.minstrel.player.PlayerController
|
||||
import com.fabledsword.minstrel.likes.data.LikesRepository
|
||||
import com.fabledsword.minstrel.playlists.data.PlaylistDetailRef
|
||||
import com.fabledsword.minstrel.playlists.data.PlaylistsRepository
|
||||
import com.fabledsword.minstrel.shared.formatDuration
|
||||
import com.fabledsword.minstrel.shared.widgets.CachedDot
|
||||
import com.fabledsword.minstrel.shared.widgets.LikeButton
|
||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||
@@ -531,7 +532,7 @@ private fun TrackRow(
|
||||
}
|
||||
row.trackId?.let { CachedDot(it) }
|
||||
Text(
|
||||
text = formatDuration(row.durationSec),
|
||||
text = formatDuration(row.durationSec, zero = "--:--"),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = rowAlpha),
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
@@ -632,16 +633,8 @@ private fun ErrorBlock(message: String, onRetry: () -> Unit) {
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
private const val SECONDS_PER_MINUTE = 60
|
||||
private const val UNAVAILABLE_ALPHA = 0.4f
|
||||
|
||||
private fun formatDuration(seconds: Int): String {
|
||||
if (seconds <= 0) return "--:--"
|
||||
val m = seconds / SECONDS_PER_MINUTE
|
||||
val s = seconds % SECONDS_PER_MINUTE
|
||||
return "$m:${s.toString().padStart(2, '0')}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Lossy mapping: PlaylistTrackRef → TrackRef so the player can take it.
|
||||
* Caller must filter `isAvailable && streamUrl != null` first.
|
||||
|
||||
+2
-3
@@ -8,6 +8,7 @@ import com.fabledsword.minstrel.cache.mutations.MutationQueue
|
||||
import com.fabledsword.minstrel.models.QuarantineRef
|
||||
import com.fabledsword.minstrel.models.TrackRef
|
||||
import com.fabledsword.minstrel.models.wire.QuarantineMineWire
|
||||
import com.fabledsword.minstrel.shared.secondsToMillis
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.datetime.Clock
|
||||
@@ -16,8 +17,6 @@ import retrofit2.create
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val MS_PER_SECOND = 1000
|
||||
|
||||
/**
|
||||
* Outcome of an `unflag` call — mirrors LikesRepository.toggleLike's
|
||||
* RequestOutcome flavor. ACCEPTED = server confirmed; QUEUED = the
|
||||
@@ -142,7 +141,7 @@ private fun TrackRef.toQuarantineEntity(reason: String, notes: String): CachedQu
|
||||
notes = notes.ifEmpty { null },
|
||||
createdAt = Clock.System.now().toString(),
|
||||
trackTitle = title,
|
||||
trackDurationMs = durationSec * MS_PER_SECOND,
|
||||
trackDurationMs = durationSec.secondsToMillis(),
|
||||
albumId = albumId,
|
||||
albumTitle = albumTitle,
|
||||
albumCoverArtPath = null,
|
||||
|
||||
Reference in New Issue
Block a user