RTSDA-iOS/Views/ConnectView.swift
RTSDA 00679f927c docs: Update README for v2.0 release and fix git remote URL
- Comprehensive README update documenting v2.0 architectural changes
- Updated git remote to ssh://rockvilleav@git.rockvilletollandsda.church:10443/RTSDA/RTSDA-iOS.git
- Documented unified ChurchService and 60% code reduction
- Added new features: Home Feed, responsive reading, enhanced UI
- Corrected license information (GPL v3 with church content copyright)
- Updated build instructions and technical stack details
2025-08-16 18:41:51 -04:00

455 lines
19 KiB
Swift

import SwiftUI
import MapKit
import CoreLocation
struct ConnectView: View {
@Environment(ChurchDataService.self) private var dataService
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@State private var churchConfig: ChurchConfig?
@State private var showingContactForm = false
var body: some View {
ScrollView {
LazyVStack(spacing: 0) {
// Hero Section
VStack(spacing: 24) {
VStack(spacing: 16) {
Text(getChurchName())
.font(.system(size: horizontalSizeClass == .regular ? 42 : 32, weight: .bold, design: .serif))
.foregroundColor(.primary)
Text("Proclaiming the Three Angels' Messages")
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .medium))
.foregroundColor(Color(hex: getBrandColor()))
.italic()
}
.padding(.top, 32)
}
.padding(.bottom, 40)
// Three Angels Messages
VStack(alignment: .leading, spacing: 24) {
Text("The Three Angels' Messages")
.font(.system(size: horizontalSizeClass == .regular ? 32 : 28, weight: .bold, design: .serif))
.padding(.horizontal, 20)
Text("Our mission is centered on the prophetic messages of Revelation 14, calling people to worship God, proclaim His truth, and prepare for Christ's second coming.")
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .regular))
.lineSpacing(4)
.padding(.horizontal, 20)
.foregroundColor(.secondary)
if horizontalSizeClass == .regular {
// iPad: Horizontal layout
HStack(spacing: 16) {
ThreeAngelsMessagesView()
}
.padding(.horizontal, 20)
} else {
// iPhone: Vertical layout
VStack(spacing: 16) {
ThreeAngelsMessagesView()
}
.padding(.horizontal, 20)
}
}
.padding(.bottom, 40)
// Worship With Us Section
VStack(alignment: .leading, spacing: 16) {
Text("Worship With Us")
.font(.system(size: horizontalSizeClass == .regular ? 28 : 24, weight: .bold))
.padding(.horizontal, 20)
ServiceTimesSection()
.padding(20)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
.padding(.horizontal, 20)
}
.padding(.bottom, 32)
// Mission Statement
VStack(alignment: .leading, spacing: 16) {
Text("Our Mission")
.font(.system(size: horizontalSizeClass == .regular ? 28 : 24, weight: .bold))
.padding(.horizontal, 20)
Text(getAboutText())
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .regular))
.lineSpacing(4)
.padding(24)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
.padding(.horizontal, 20)
NavigationLink {
BeliefsView()
} label: {
HStack {
Text("Our 28 Fundamental Beliefs")
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .semibold))
Spacer()
Image(systemName: "chevron.right")
}
.foregroundColor(.white)
.padding(20)
.background(Color(hex: getBrandColor()), in: RoundedRectangle(cornerRadius: 12))
}
.padding(.horizontal, 20)
}
.padding(.bottom, 32)
// Visit & Connect Section
VStack(alignment: .leading, spacing: 16) {
Text("Visit & Connect")
.font(.system(size: horizontalSizeClass == .regular ? 28 : 24, weight: .bold))
.padding(.horizontal, 20)
// Interactive Map
ChurchMapView()
.padding(.horizontal, 20)
// Contact Information
VStack(spacing: 12) {
ContactActionRow(
icon: "location.fill",
title: "Visit Us",
subtitle: getChurchAddress(),
iconColor: .red,
action: ContactActions.directionsAction(address: getChurchAddress().replacingOccurrences(of: " ", with: "+"))
)
if UIDevice.current.userInterfaceIdiom == .phone {
ContactActionRow(
icon: "phone.fill",
title: "Call Us",
subtitle: getContactPhone(),
iconColor: .green,
action: ContactActions.callAction(phoneNumber: getContactPhone())
)
}
ContactActionRow(
icon: "envelope.fill",
title: "Email Us",
subtitle: getContactEmail(),
iconColor: .blue,
action: ContactActions.emailAction(email: getContactEmail())
)
}
.padding(20)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
.padding(.horizontal, 20)
// Send Message Button
Button {
showingContactForm = true
} label: {
HStack {
Image(systemName: "paperplane.fill")
Text("Send Us a Message")
.fontWeight(.semibold)
}
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding(16)
.background(Color(hex: getBrandColor()), in: RoundedRectangle(cornerRadius: 12))
}
.padding(.horizontal, 20)
}
.padding(.bottom, 32)
// Support Our Ministry Section
VStack(alignment: .leading, spacing: 16) {
Text("Support Our Ministry")
.font(.system(size: horizontalSizeClass == .regular ? 28 : 24, weight: .bold))
.padding(.horizontal, 20)
Text("Your generous gifts help us share the Three Angels' Messages and serve our community.")
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .regular))
.lineSpacing(4)
.padding(.horizontal, 20)
.foregroundColor(.secondary)
Link(destination: URL(string: getDonationUrl())!) {
HStack {
Image(systemName: "heart.fill")
Text("Give Securely Online")
.fontWeight(.semibold)
}
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding(16)
.background(.green, in: RoundedRectangle(cornerRadius: 12))
}
.padding(.horizontal, 20)
HStack {
Image(systemName: "lock.shield.fill")
.foregroundColor(.green)
Text("Secure giving powered by Adventist Giving")
.font(.caption)
.foregroundColor(.secondary)
}
.padding(.horizontal, 20)
}
.padding(.bottom, 100)
}
}
.navigationTitle("About Us")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
loadChurchConfig()
}
.sheet(isPresented: $showingContactForm) {
ContactFormView(isModal: true)
.environment(dataService)
}
}
private func loadChurchConfig() {
// Use Rust functions directly - NO JSON parsing in Swift!
self.churchConfig = ChurchConfig(
contactPhone: getContactPhone(),
contactEmail: getContactEmail(),
churchAddress: getChurchAddress(),
churchName: getChurchName()
)
}
}
struct ChurchMapView: View {
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
// Church location coordinates from Rust
private var churchLocation: CLLocationCoordinate2D {
let coords = getCoordinates()
return CLLocationCoordinate2D(
latitude: coords[0],
longitude: coords[1]
)
}
@State private var cameraPosition: MapCameraPosition = .region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 41.8703594, longitude: -72.4077036),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
)
)
var body: some View {
Map(position: $cameraPosition) {
Annotation("Rockville Tolland SDA Church", coordinate: churchLocation) {
VStack {
Image(systemName: "house.fill")
.font(.title2)
.foregroundColor(.white)
.padding(8)
.background(Color(hex: getBrandColor()), in: Circle())
.shadow(radius: 4)
Text("RTSDA")
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.black.opacity(0.8), in: Capsule())
.shadow(radius: 3)
}
}
}
.frame(height: horizontalSizeClass == .regular ? 250 : 200)
.cornerRadius(16)
.onTapGesture {
// Open in Apple Maps
let placemark = MKPlacemark(coordinate: churchLocation)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = "Rockville Tolland SDA Church"
mapItem.openInMaps(launchOptions: [
MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
])
}
.onAppear {
// Update camera position when config loads
cameraPosition = .region(
MKCoordinateRegion(
center: churchLocation,
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
)
)
}
}
}
struct AngelMessageCard: View {
let number: Int
let title: String
let reference: String
let description: String
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
var body: some View {
VStack(spacing: 12) {
// Number circle
Text("\(number)")
.font(.system(size: horizontalSizeClass == .regular ? 28 : 24, weight: .bold))
.foregroundColor(.white)
.frame(width: horizontalSizeClass == .regular ? 50 : 44, height: horizontalSizeClass == .regular ? 50 : 44)
.background(Color(hex: getBrandColor()), in: Circle())
VStack(spacing: 8) {
Text(title)
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16, weight: .semibold))
.multilineTextAlignment(.center)
Text(reference)
.font(.system(size: horizontalSizeClass == .regular ? 14 : 12, weight: .medium))
.foregroundColor(Color(hex: getBrandColor()))
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color(hex: getBrandColor()).opacity(0.1), in: Capsule())
Text(description)
.font(.system(size: horizontalSizeClass == .regular ? 15 : 13))
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.lineLimit(4)
}
}
.padding(horizontalSizeClass == .regular ? 20 : 16)
.frame(maxWidth: .infinity)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
}
}
struct ThreeAngelsMessagesView: View {
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@State private var messages: [(title: String, reference: String, description: String)] = [
("First Angel's Message", "Revelation 14:6-7", "Fear God and give glory to Him, for the hour of His judgment has come"),
("Second Angel's Message", "Revelation 14:8", "Babylon is fallen, is fallen, that great city"),
("Third Angel's Message", "Revelation 14:9-12", "Keep the commandments of God and the faith of Jesus")
]
@State private var isLoading = true
private let angelMessages = [
(title: "First Angel's Message", reference: "Revelation 14:6-7"),
(title: "Second Angel's Message", reference: "Revelation 14:8"),
(title: "Third Angel's Message", reference: "Revelation 14:9-12")
]
var body: some View {
ForEach(Array(messages.enumerated()), id: \.offset) { index, message in
NavigationLink {
AngelMessageDetailView(
number: index + 1,
title: message.title,
reference: message.reference
)
} label: {
AngelMessageCard(
number: index + 1,
title: message.title,
reference: message.reference,
description: message.description
)
}
.buttonStyle(PlainButtonStyle())
}
.task {
await loadMessages()
}
}
private func loadMessages() async {
var loadedMessages: [(String, String, String)] = []
for angel in angelMessages {
// Use church-core to fetch the actual Bible verse text (following BeliefsView pattern)
let versesJson = fetchBibleVerseJson(query: angel.reference)
// Use Rust function for JSON parsing and description generation - NO business logic in Swift!
let description = generateVerseDescription(versesJson: versesJson)
loadedMessages.append((angel.title, angel.reference, description))
}
await MainActor.run {
messages = loadedMessages
isLoading = false
}
}
}
struct AngelMessageDetailView: View {
let number: Int
let title: String
let reference: String
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
@State private var verseText: String = ""
@State private var isLoading: Bool = true
var body: some View {
ScrollView {
VStack(spacing: 24) {
// Header with number circle
VStack(spacing: 16) {
Text("\(number)")
.font(.system(size: horizontalSizeClass == .regular ? 48 : 40, weight: .bold))
.foregroundColor(.white)
.frame(width: horizontalSizeClass == .regular ? 80 : 70, height: horizontalSizeClass == .regular ? 80 : 70)
.background(Color(hex: getBrandColor()), in: Circle())
Text(title)
.font(.system(size: horizontalSizeClass == .regular ? 32 : 28, weight: .bold, design: .serif))
.multilineTextAlignment(.center)
Text(reference)
.font(.system(size: horizontalSizeClass == .regular ? 20 : 18, weight: .medium))
.foregroundColor(Color(hex: getBrandColor()))
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color(hex: getBrandColor()).opacity(0.1), in: Capsule())
}
// Verse content
VStack(alignment: .leading, spacing: 16) {
if isLoading {
ProgressView("Loading verse...")
} else if !verseText.isEmpty {
Text(verseText)
.font(.system(size: horizontalSizeClass == .regular ? 20 : 18, weight: .regular))
.lineSpacing(6)
.padding(24)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
} else {
Text("Unable to load verse text")
.font(.system(size: horizontalSizeClass == .regular ? 18 : 16))
.foregroundColor(.secondary)
.padding(24)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
}
}
}
.padding(.horizontal, 20)
.padding(.bottom, 100)
}
.navigationTitle("Angel's Message")
.navigationBarTitleDisplayMode(.inline)
.task {
await loadVerse()
}
}
private func loadVerse() async {
let versesJson = fetchBibleVerseJson(query: reference)
await MainActor.run {
isLoading = false
// Use Rust function for JSON parsing and text extraction - NO business logic in Swift!
verseText = extractFullVerseText(versesJson: versesJson)
}
}
}