144 lines
5.7 KiB
Swift
144 lines
5.7 KiB
Swift
import SwiftUI
|
|
|
|
struct SplashScreenView: View {
|
|
@StateObject private var configService = ConfigService.shared
|
|
@State private var isActive = false
|
|
@State private var size = 0.8
|
|
@State private var opacity = 0.5
|
|
@State private var bibleVerse = ""
|
|
@State private var bibleReference = ""
|
|
|
|
// Timing constants
|
|
private let fadeInDuration: Double = 0.5
|
|
private let baseDisplayDuration: Double = 1.0
|
|
private let timePerWord: Double = 0.15
|
|
private let minDisplayDuration: Double = 1.5
|
|
private let maxDisplayDuration: Double = 3.0
|
|
|
|
private func calculateDisplayDuration(for text: String) -> Double {
|
|
let words = text.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty }
|
|
let calculatedDuration = baseDisplayDuration + (Double(words.count) * timePerWord)
|
|
return min(max(calculatedDuration, minDisplayDuration), maxDisplayDuration)
|
|
}
|
|
|
|
var body: some View {
|
|
if isActive {
|
|
ContentView()
|
|
} else {
|
|
ZStack {
|
|
LinearGradient(gradient: Gradient(colors: [
|
|
Color(hex: "3b0d11"),
|
|
Color(hex: "21070a")
|
|
]), startPoint: .top, endPoint: .bottom)
|
|
.ignoresSafeArea()
|
|
|
|
VStack(spacing: 20) {
|
|
Image("sdalogo")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 80, height: 80)
|
|
|
|
Text("Rockville-Tolland SDA Church")
|
|
.font(.custom("Montserrat-SemiBold", size: 24))
|
|
.foregroundColor(.white)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Rectangle()
|
|
.fill(Color(hex: "fb8b23"))
|
|
.frame(width: 60, height: 2)
|
|
|
|
Text(bibleVerse)
|
|
.font(.custom("Lora-Italic", size: 18))
|
|
.foregroundColor(.white.opacity(0.8))
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 20)
|
|
.lineSpacing(4)
|
|
|
|
Text(bibleReference)
|
|
.font(.custom("Montserrat-Regular", size: 14))
|
|
.foregroundColor(Color(hex: "fb8b23"))
|
|
}
|
|
.padding()
|
|
.scaleEffect(size)
|
|
.opacity(opacity)
|
|
.task {
|
|
// First load config
|
|
await configService.loadConfig()
|
|
|
|
do {
|
|
let verse = try await BibleService.shared.getRandomVerse()
|
|
bibleVerse = verse.verse
|
|
bibleReference = verse.reference
|
|
|
|
// Calculate display duration based on verse length
|
|
let displayDuration = calculateDisplayDuration(for: verse.verse)
|
|
|
|
// Start fade in animation after verse is loaded
|
|
withAnimation(.easeIn(duration: fadeInDuration)) {
|
|
self.size = 0.9
|
|
self.opacity = 1.0
|
|
}
|
|
|
|
// Wait for fade in + calculated display duration before transitioning
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + fadeInDuration + displayDuration) {
|
|
withAnimation {
|
|
self.isActive = true
|
|
}
|
|
}
|
|
} catch {
|
|
// Fallback to a default verse if API fails
|
|
bibleVerse = "For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life."
|
|
bibleReference = "John 3:16"
|
|
|
|
// Calculate duration for fallback verse
|
|
let displayDuration = calculateDisplayDuration(for: bibleVerse)
|
|
|
|
// Use same timing for fallback verse
|
|
withAnimation(.easeIn(duration: fadeInDuration)) {
|
|
self.size = 0.9
|
|
self.opacity = 1.0
|
|
}
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + fadeInDuration + displayDuration) {
|
|
withAnimation {
|
|
self.isActive = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Color {
|
|
init(hex: String) {
|
|
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
|
var int: UInt64 = 0
|
|
Scanner(string: hex).scanHexInt64(&int)
|
|
let a, r, g, b: UInt64
|
|
switch hex.count {
|
|
case 3: // RGB (12-bit)
|
|
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
|
case 6: // RGB (24-bit)
|
|
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
|
case 8: // ARGB (32-bit)
|
|
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
|
default:
|
|
(a, r, g, b) = (1, 1, 1, 0)
|
|
}
|
|
|
|
self.init(
|
|
.sRGB,
|
|
red: Double(r) / 255,
|
|
green: Double(g) / 255,
|
|
blue: Double(b) / 255,
|
|
opacity: Double(a) / 255
|
|
)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
SplashScreenView()
|
|
}
|