Files

147 lines
5.8 KiB
Swift

import SwiftUI
struct SessionView: View {
@EnvironmentObject private var analytics: HermesAnalyticsClient
@EnvironmentObject private var localization: LocalizationStore
@EnvironmentObject private var repository: HermesRepository
let onRetry: () -> Void
var body: some View {
let session = repository.currentSession
let bannerMessage = hermesUserFacingErrorMessage(
localization: localization,
localeCode: localization.localeCode,
error: repository.errorCause
)
let statusText: String
if session != nil {
statusText = localization.string(for: "session.status_ready")
} else if bannerMessage != nil {
statusText = localization.string(for: "session.status_error")
} else {
statusText = localization.string(for: "session.status_loading")
}
return VStack(alignment: .leading, spacing: HermesTheme.sectionSpacing) {
HermesSectionHeader(
title: localization.string(for: "session.title"),
subtitle: localization.string(for: "session.subtitle")
)
if session == nil {
if let bannerMessage {
sessionErrorState(
message: bannerMessage,
retryText: localization.string(for: "common.retry"),
onRetry: onRetry
)
} else {
sessionLoadingState(
title: statusText,
subtitle: localization.string(for: "session.note")
)
}
} else {
sessionStatusBadge(text: statusText, warning: bannerMessage != nil)
if let bannerMessage {
sessionBanner(message: bannerMessage)
}
sessionRow(label: localization.string(for: "session.id_label"), value: session?.sessionId.uuidString ?? "--")
sessionRow(label: localization.string(for: "session.user_id_label"), value: session?.userId.uuidString ?? "--")
sessionRow(
label: localization.string(for: "session.locale_label"),
value: localization.localeName(for: session?.localeCode ?? localization.localeCode)
)
sessionRow(label: localization.string(for: "session.started_label"), value: session.map { Self.compactDateFormatter.string(from: $0.startedAt) } ?? "--")
sessionRow(label: localization.string(for: "session.variant_label"), value: session?.experimentVariant ?? "--")
sessionRow(label: localization.string(for: "session.app_version_label"), value: session?.appVersion ?? "--")
sessionRow(label: localization.string(for: "session.device_model_label"), value: session?.deviceModel ?? "--")
sessionRow(label: localization.string(for: "session.os_version_label"), value: session?.osVersion ?? "--")
Text(localization.string(for: "session.note"))
.font(.callout)
.foregroundStyle(HermesTheme.textSecondary)
}
}
.hermesCard()
.onAppear {
analytics.track("screen_viewed", attributes: ["screen_name": "session"])
}
}
@ViewBuilder
private func sessionLoadingState(title: String, subtitle: String) -> some View {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.headline.weight(.semibold))
.foregroundStyle(HermesTheme.textPrimary)
Text(subtitle)
.font(.callout)
.foregroundStyle(HermesTheme.textSecondary)
}
}
@ViewBuilder
private func sessionErrorState(message: String, retryText: String, onRetry: @escaping () -> Void) -> some View {
VStack(alignment: .leading, spacing: 12) {
Text(message)
.font(.callout)
.foregroundStyle(HermesTheme.warning)
Button {
onRetry()
} label: {
Text(retryText)
}
.buttonStyle(HermesSecondaryButtonStyle())
}
}
@ViewBuilder
private func sessionStatusBadge(text: String, warning: Bool) -> some View {
Text(text)
.font(.caption.weight(.semibold))
.foregroundStyle(warning ? HermesTheme.warning : HermesTheme.accent)
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background((warning ? HermesTheme.warning : HermesTheme.accent).opacity(0.16))
.clipShape(Capsule())
}
@ViewBuilder
private func sessionBanner(message: String) -> some View {
Text(message)
.font(.callout)
.foregroundStyle(HermesTheme.warning)
.padding(.horizontal, 12)
.padding(.vertical, 10)
.frame(maxWidth: .infinity, alignment: .leading)
.background(HermesTheme.warning.opacity(0.12))
.clipShape(RoundedRectangle(cornerRadius: HermesTheme.insetRadius, style: .continuous))
}
@ViewBuilder
private func sessionRow(label: String, value: String) -> some View {
HStack {
Text(label)
.font(.callout)
.foregroundStyle(HermesTheme.textSecondary)
Spacer(minLength: 12)
Text(value)
.font(.callout.weight(.semibold))
.foregroundStyle(HermesTheme.textPrimary)
}
}
private static let compactDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = .current
formatter.dateFormat = "yyyy-MM-dd HH:mm"
return formatter
}()
}