redesign iOS round into fullscreen swipe flow

This commit is contained in:
2026-04-10 10:23:15 +02:00
parent 2553845100
commit 7b466f0a34
12 changed files with 685 additions and 254 deletions
+177
View File
@@ -10,11 +10,40 @@ final class HermesRepository: ObservableObject {
@Published private(set) var serverClockOffset: TimeInterval?
private let apiClient: HermesAPIClient
private var mockRoundIndex = 0
init(apiClient: HermesAPIClient) {
self.apiClient = apiClient
}
func reset() {
currentSession = nil
currentRound = nil
isLoading = false
errorCause = nil
serverClockOffset = nil
mockRoundIndex = 0
}
func bootstrapMock(localeCode: String, appVersion: String) async {
isLoading = true
errorCause = nil
mockRoundIndex = 0
currentSession = MockHermesData.session(localeCode: localeCode, appVersion: appVersion)
currentRound = MockHermesData.round(index: mockRoundIndex, now: Date())
isLoading = false
}
func refreshMockRound() async throws -> HermesRound {
isLoading = true
errorCause = nil
mockRoundIndex = (mockRoundIndex + 1) % MockHermesData.roundCount
let round = MockHermesData.round(index: mockRoundIndex, now: Date())
currentRound = round
isLoading = false
return round
}
func bootstrap(_ request: HermesSessionStartRequest) async throws -> HermesSessionResponse {
isLoading = true
errorCause = nil
@@ -143,3 +172,151 @@ final class HermesRepository: ObservableObject {
)
}
}
private enum MockHermesData {
static let roundCount = 3
static func session(localeCode: String, appVersion: String) -> HermesSessionResponse {
HermesSessionResponse(
sessionId: UUID(uuidString: "00000000-0000-0000-0000-000000000001") ?? UUID(),
userId: UUID(uuidString: "00000000-0000-0000-0000-000000000002") ?? UUID(),
startedAt: Date(),
endedAt: nil,
experimentVariant: "modern",
appVersion: appVersion,
deviceModel: "Demo Device",
osVersion: "Demo OS",
localeCode: localeCode,
devicePlatform: "ios"
)
}
static func round(index: Int, now: Date) -> HermesRound {
let scenarios = [
scenario(
eventID: "10000000-0000-0000-0000-000000000001",
marketID: "20000000-0000-0000-0000-000000000001",
yesOutcomeID: "30000000-0000-0000-0000-000000000001",
noOutcomeID: "30000000-0000-0000-0000-000000000002",
promptKey: "event.question.score",
sourceRef: "mock-score",
winningOutcomeID: "30000000-0000-0000-0000-000000000001",
now: now
),
scenario(
eventID: "10000000-0000-0000-0000-000000000011",
marketID: "20000000-0000-0000-0000-000000000011",
yesOutcomeID: "30000000-0000-0000-0000-000000000011",
noOutcomeID: "30000000-0000-0000-0000-000000000012",
promptKey: "event.question.save",
sourceRef: "mock-save",
winningOutcomeID: "30000000-0000-0000-0000-000000000012",
now: now
),
scenario(
eventID: "10000000-0000-0000-0000-000000000021",
marketID: "20000000-0000-0000-0000-000000000021",
yesOutcomeID: "30000000-0000-0000-0000-000000000021",
noOutcomeID: "30000000-0000-0000-0000-000000000022",
promptKey: "event.question.convert",
sourceRef: "mock-convert",
winningOutcomeID: "30000000-0000-0000-0000-000000000021",
now: now
),
]
return scenarios[index % scenarios.count]
}
private static func scenario(
eventID: String,
marketID: String,
yesOutcomeID: String,
noOutcomeID: String,
promptKey: String,
sourceRef: String,
winningOutcomeID: String,
now: Date
) -> HermesRound {
let lockAt = now.addingTimeInterval(15)
let settleAt = lockAt.addingTimeInterval(6)
let mediaURL = URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8")!
let event = HermesEvent(
id: UUID(uuidString: eventID) ?? UUID(),
sportType: "football",
sourceRef: sourceRef,
titleEn: promptKey,
titleSv: promptKey,
status: "active",
previewStartMs: 0,
previewEndMs: 12_000,
revealStartMs: 12_000,
revealEndMs: 18_000,
lockAt: lockAt,
settleAt: settleAt
)
let media = HermesEventMedia(
id: UUID(),
eventId: event.id,
mediaType: "hls_main",
hlsMasterUrl: mediaURL,
posterUrl: nil,
durationMs: 18_000,
previewStartMs: 0,
previewEndMs: 12_000,
revealStartMs: 12_000,
revealEndMs: 18_000
)
let marketUUID = UUID(uuidString: marketID) ?? UUID()
let market = HermesMarket(
id: marketUUID,
eventId: event.id,
questionKey: promptKey,
marketType: promptKey,
status: "open",
lockAt: lockAt,
settlementRuleKey: "demo",
outcomes: [
HermesOutcome(
id: UUID(uuidString: yesOutcomeID) ?? UUID(),
marketId: marketUUID,
outcomeCode: "yes",
labelKey: "round.yes",
sortOrder: 0
),
HermesOutcome(
id: UUID(uuidString: noOutcomeID) ?? UUID(),
marketId: marketUUID,
outcomeCode: "no",
labelKey: "round.no",
sortOrder: 1
),
]
)
let oddsVersionID = UUID()
let oddsVersion = HermesOddsVersion(
id: oddsVersionID,
marketId: market.id,
versionNo: 1,
createdAt: now,
isCurrent: true,
odds: [
HermesOutcomeOdds(id: UUID(), oddsVersionId: oddsVersionID, outcomeId: market.outcomes[0].id, decimalOdds: 1.82, fractionalNum: 91, fractionalDen: 50),
HermesOutcomeOdds(id: UUID(), oddsVersionId: oddsVersionID, outcomeId: market.outcomes[1].id, decimalOdds: 2.05, fractionalNum: 41, fractionalDen: 20),
]
)
let settlement = HermesSettlement(
id: UUID(),
marketId: market.id,
settledAt: settleAt,
winningOutcomeId: UUID(uuidString: winningOutcomeID) ?? market.outcomes[0].id
)
return HermesRound(event: event, media: media, market: market, oddsVersion: oddsVersion, settlement: settlement)
}
}