diff --git a/mobile/ios-app/App/HermesRepository.swift b/mobile/ios-app/App/HermesRepository.swift index 5fbe45a..d657dcf 100644 --- a/mobile/ios-app/App/HermesRepository.swift +++ b/mobile/ios-app/App/HermesRepository.swift @@ -1,3 +1,4 @@ +import AVFoundation import Combine import Foundation @@ -239,9 +240,12 @@ enum MockHermesData { winningOutcomeID: String, now: Date ) -> HermesRound { - let lockAt = now.addingTimeInterval(15) - let settleAt = lockAt.addingTimeInterval(6) let mediaURL = localMediaURL(named: "example-round.mov") ?? fallbackMediaURL + let previewDurationMs = assetDurationMs(url: mediaURL) ?? 15_000 + let revealURL = revealMediaURL() + let revealDurationMs = assetDurationMs(url: revealURL) ?? 6_000 + let lockAt = now.addingTimeInterval(Double(previewDurationMs) / 1_000.0) + let settleAt = lockAt.addingTimeInterval(6) let event = HermesEvent( id: UUID(uuidString: eventID) ?? UUID(), @@ -251,9 +255,9 @@ enum MockHermesData { titleSv: promptKey, status: "active", previewStartMs: 0, - previewEndMs: 12_000, - revealStartMs: 12_000, - revealEndMs: 18_000, + previewEndMs: previewDurationMs, + revealStartMs: 0, + revealEndMs: revealDurationMs, lockAt: lockAt, settleAt: settleAt ) @@ -264,11 +268,11 @@ enum MockHermesData { mediaType: "hls_main", hlsMasterUrl: mediaURL, posterUrl: nil, - durationMs: 18_000, + durationMs: previewDurationMs, previewStartMs: 0, - previewEndMs: 12_000, - revealStartMs: 12_000, - revealEndMs: 18_000 + previewEndMs: previewDurationMs, + revealStartMs: 0, + revealEndMs: revealDurationMs ) let marketUUID = UUID(uuidString: marketID) ?? UUID() @@ -336,4 +340,13 @@ enum MockHermesData { let candidate = repoRoot.appendingPathComponent(fileName) return FileManager.default.fileExists(atPath: candidate.path) ? candidate : nil } + + private static func assetDurationMs(url: URL) -> Int? { + let duration = AVAsset(url: url).duration.seconds + guard duration.isFinite, duration > 0 else { + return nil + } + + return Int((duration * 1_000.0).rounded()) + } } diff --git a/mobile/ios-app/Features/Round/RoundView.swift b/mobile/ios-app/Features/Round/RoundView.swift index 08a85da..716bb66 100644 --- a/mobile/ios-app/Features/Round/RoundView.swift +++ b/mobile/ios-app/Features/Round/RoundView.swift @@ -38,7 +38,8 @@ struct RoundView: View { TimelineView(.periodic(from: Date(), by: 1)) { _ in let round = repository.currentRound let now = repository.serverNow() - let remaining = max(lockAt.timeIntervalSince(now), 0) + let activeLockAt = round?.event.lockAt ?? lockAt + let remaining = max(activeLockAt.timeIntervalSince(now), 0) let timerLocked = round != nil && remaining <= 0 let bannerMessage = actionMessage ?? hermesUserFacingErrorMessage( localization: localization, @@ -118,7 +119,7 @@ struct RoundView: View { Spacer(minLength: 12) - SemiCircleCountdownView(progress: progressValue(for: remaining), label: Self.countdownText(for: remaining)) + SemiCircleCountdownView(progress: progressValue(for: remaining, round: round), label: Self.countdownText(for: remaining)) } .padding(.horizontal, HermesTheme.screenPadding) .padding(.top, 112) @@ -382,7 +383,7 @@ struct RoundView: View { } phase = .reveal - playerCoordinator.play(url: revealMediaURL(for: round), startTimeMs: round.media.revealStartMs, rate: 1.0) + playerCoordinator.play(url: revealMediaURL(for: round), startTimeMs: revealStartTimeMs(for: round), rate: 1.0) analytics.track("reveal_started", attributes: roundAnalyticsAttributes(round).merging(baseSelectionAttributes(selectedOutcomeID)) { _, new in new }) } } catch { @@ -430,6 +431,10 @@ struct RoundView: View { repository.currentSession?.deviceModel == "Demo Device" ? MockHermesData.revealMediaURL() : round.media.hlsMasterUrl } + private func revealStartTimeMs(for round: HermesRound) -> Int { + repository.currentSession?.deviceModel == "Demo Device" ? 0 : round.media.revealStartMs + } + private func promptTitle(for round: HermesRound?) -> String { guard let round else { return localization.string(for: "common.loading") @@ -488,8 +493,9 @@ struct RoundView: View { return outcomeTitle(outcome) } - private func progressValue(for remaining: TimeInterval) -> Double { - min(max(remaining / 15.0, 0), 1) + private func progressValue(for remaining: TimeInterval, round: HermesRound?) -> Double { + let totalDuration = round.map { Double(max($0.media.previewEndMs - $0.media.previewStartMs, 1)) / 1_000.0 } ?? 15.0 + return min(max(remaining / totalDuration, 0), 1) } private static func countdownText(for remaining: TimeInterval) -> String {