Lascia trasmissibili le partite anche dopo l'orario previsto.

Se l'operatore è in ritardo deve ancora trovare la gara nell'app: restano in hub per tutta la giornata e, nei tornei aperti, fino alla fine dell'evento.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-06 10:07:23 +02:00
co-authored by Cursor
parent c3ef8e91a6
commit 24c6ce4da0
10 changed files with 91 additions and 14 deletions
@@ -51,3 +51,11 @@ private fun parseInstantOrNull(value: String): Instant? {
}
fun Instant.isScheduledFuture(now: Instant = Instant.now()): Boolean = isAfter(now)
fun Instant.isScheduledOnCalendar(
now: Instant = Instant.now(),
zone: ZoneId = ZoneId.of("Europe/Rome"),
): Boolean {
val startOfToday = now.atZone(zone).toLocalDate().atStartOfDay(zone).toInstant()
return !isBefore(startOfToday)
}
@@ -1,10 +1,10 @@
package com.matchlivetv.match_live_tv.domain
import com.matchlivetv.match_live_tv.core.isScheduledFuture
import com.matchlivetv.match_live_tv.core.isScheduledOnCalendar
import com.matchlivetv.match_live_tv.core.parseApiInstant
import java.time.Instant
/** Partite visibili nell'hub coach: dirette attive, wizard in corso, programmate future. */
/** Partite visibili nell'hub coach: dirette attive, wizard in corso, in calendario oggi o future. */
fun Match.isCoachHubVisible(now: Instant = Instant.now()): Boolean {
coachHubVisible?.let { return it }
@@ -15,7 +15,7 @@ fun Match.isCoachHubVisible(now: Instant = Instant.now()): Boolean {
if (streamCompleted) return false
val scheduled = parseApiInstant(scheduledAt)
return scheduled != null && scheduled.isScheduledFuture(now)
return scheduled != null && scheduled.isScheduledOnCalendar(now)
}
fun List<Match>.coachHubVisible(now: Instant = Instant.now()): List<Match> =
@@ -47,8 +47,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.matchlivetv.match_live_tv.R
import com.matchlivetv.match_live_tv.core.isScheduledFuture
import com.matchlivetv.match_live_tv.core.parseApiInstant
import com.matchlivetv.match_live_tv.data.AppContainer
import com.matchlivetv.match_live_tv.data.repository.MatchSessionLauncher
import com.matchlivetv.match_live_tv.domain.Match
@@ -161,7 +159,7 @@ fun MatchesScreen(
(match.hasActiveSession && match.activeSessionStatus == "idle")
}
val scheduledMatches = matches.filter { match ->
!match.hasActiveSession && parseApiInstant(match.scheduledAt)?.isScheduledFuture() == true
!match.hasActiveSession && !match.scheduledAt.isNullOrBlank()
}
val draftMatches = matches.filter { !it.hasActiveSession && it.scheduledAt == null }
val calendarMatches = (scheduledMatches + draftMatches).distinctBy { it.id }
@@ -10,7 +10,22 @@ class ApiInstantTest {
fun parseApiInstant_railsOffsetWithMillis() {
val instant = parseApiInstant("2026-06-06T20:00:00.000+02:00")
assertNotNull(instant)
assertTrue(instant!!.isScheduledFuture(Instant.parse("2026-06-06T16:00:00Z")))
assertTrue(instant!!.isScheduledOnCalendar(Instant.parse("2026-06-06T16:00:00Z")))
}
@Test
fun isScheduledOnCalendar_keepsThisMorningAfterKickoff() {
val kickoff = Instant.parse("2026-09-06T07:00:00Z")
val now = Instant.parse("2026-09-06T08:30:00Z")
assertTrue(kickoff.isScheduledOnCalendar(now))
assertTrue(!kickoff.isScheduledFuture(now))
}
@Test
fun isScheduledOnCalendar_hidesYesterday() {
val yesterday = Instant.parse("2026-09-05T10:00:00Z")
val now = Instant.parse("2026-09-06T08:30:00Z")
assertTrue(!yesterday.isScheduledOnCalendar(now))
}
@Test
@@ -19,6 +19,14 @@ enum ApiInstant {
return date > now
}
static func isScheduledOnCalendar(_ raw: String?, now: Date = Date()) -> Bool {
guard let date = parse(raw) else { return false }
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "Europe/Rome") ?? .current
let startOfToday = calendar.startOfDay(for: now)
return date >= startOfToday
}
static func formatMatchDate(_ raw: String?) -> String? {
guard let date = parse(raw) else { return nil }
let f = DateFormatter()
@@ -12,13 +12,17 @@ enum MatchHubFilter {
}
if match.streamCompleted { return false }
return ApiInstant.isScheduledFuture(match.scheduledAt, now: now)
return ApiInstant.isScheduledOnCalendar(match.scheduledAt, now: now)
}
static func isScheduledFuture(_ match: Match, now: Date = Date()) -> Bool {
ApiInstant.isScheduledFuture(match.scheduledAt, now: now)
}
static func isOnCalendar(_ match: Match, now: Date = Date()) -> Bool {
ApiInstant.isScheduledOnCalendar(match.scheduledAt, now: now)
}
static func isDraft(_ match: Match) -> Bool {
!match.hasActiveSession && (match.scheduledAt == nil || match.scheduledAt?.isEmpty == true)
}
@@ -289,7 +289,7 @@ struct MatchesScreen: View {
}
private var scheduledMatches: [Match] {
matches.filter { !$0.hasActiveSession && MatchHubFilter.isScheduledFuture($0) }
matches.filter { !$0.hasActiveSession && !($0.scheduledAt ?? "").isEmpty }
}
private var calendarMatches: [Match] {