147 lines
5.8 KiB
Swift
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
|
|
}()
|
|
}
|