
- 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
213 lines
8 KiB
Swift
213 lines
8 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - Scripture Sheet Components
|
|
|
|
struct ScriptureSheet: View {
|
|
@Binding var scriptureText: String
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
init(scriptureText: Binding<String>) {
|
|
self._scriptureText = scriptureText
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 20) {
|
|
if scriptureText.isEmpty {
|
|
Text("No scripture text available")
|
|
.foregroundStyle(.secondary)
|
|
.padding()
|
|
} else {
|
|
let sections = formatScriptureText(scriptureText)
|
|
|
|
ForEach(Array(sections.enumerated()), id: \.offset) { index, section in
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
// Verse text
|
|
Text(section.verse)
|
|
.font(.body)
|
|
.lineSpacing(4)
|
|
#if os(iOS)
|
|
.textSelection(.enabled)
|
|
#endif
|
|
|
|
// Reference
|
|
if !section.reference.isEmpty {
|
|
Text(section.reference)
|
|
.font(.caption)
|
|
.fontWeight(.medium)
|
|
.foregroundStyle(.secondary)
|
|
.padding(.top, 4)
|
|
}
|
|
}
|
|
.padding(.horizontal)
|
|
|
|
// Add divider between sections (except last)
|
|
if index < sections.count - 1 {
|
|
Divider()
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
|
|
Spacer(minLength: 20)
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.navigationTitle("Scripture Reading")
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button("Done") {
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.presentationDetents([.medium, .large])
|
|
.presentationDragIndicator(.visible)
|
|
}
|
|
|
|
private func formatScriptureText(_ text: String) -> [ScriptureSection] {
|
|
// Use the Rust implementation for consistent formatting across platforms
|
|
let jsonString = formatScriptureTextJson(scriptureText: text)
|
|
guard let data = jsonString.data(using: .utf8),
|
|
let sections = try? JSONDecoder().decode([ScriptureSection].self, from: data) else {
|
|
// Fallback if JSON parsing fails
|
|
return [ScriptureSection(verse: text, reference: "")]
|
|
}
|
|
return sections
|
|
}
|
|
}
|
|
|
|
// MARK: - Scripture Sheet with Sermon ID (loads data automatically)
|
|
|
|
struct ScriptureSheetForSermon: View {
|
|
let sermonId: String
|
|
@Environment(\.dismiss) private var dismiss
|
|
@State private var scriptureText: String = ""
|
|
@State private var isLoading = true
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 20) {
|
|
if isLoading {
|
|
ProgressView("Loading scripture...")
|
|
.padding()
|
|
} else if scriptureText.isEmpty {
|
|
Text("No scripture text available for this sermon")
|
|
.foregroundStyle(.secondary)
|
|
.padding()
|
|
} else {
|
|
let sections = formatScriptureText(scriptureText)
|
|
|
|
ForEach(Array(sections.enumerated()), id: \.offset) { index, section in
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
// Verse text
|
|
Text(section.verse)
|
|
.font(.body)
|
|
.lineSpacing(4)
|
|
#if os(iOS)
|
|
.textSelection(.enabled)
|
|
#endif
|
|
|
|
// Reference
|
|
if !section.reference.isEmpty {
|
|
Text(section.reference)
|
|
.font(.caption)
|
|
.fontWeight(.medium)
|
|
.foregroundStyle(.secondary)
|
|
.padding(.top, 4)
|
|
}
|
|
}
|
|
.padding(.horizontal)
|
|
|
|
// Add divider between sections (except last)
|
|
if index < sections.count - 1 {
|
|
Divider()
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
|
|
Spacer(minLength: 20)
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.navigationTitle("Scripture Reading")
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button("Done") {
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.presentationDetents([.medium, .large])
|
|
.presentationDragIndicator(.visible)
|
|
.task {
|
|
isLoading = true
|
|
scriptureText = fetchScriptureVersesForSermonJson(sermonId: sermonId)
|
|
isLoading = false
|
|
}
|
|
}
|
|
|
|
private func formatScriptureText(_ text: String) -> [ScriptureSection] {
|
|
// Use the Rust implementation for consistent formatting across platforms
|
|
let jsonString = formatScriptureTextJson(scriptureText: text)
|
|
guard let data = jsonString.data(using: .utf8),
|
|
let sections = try? JSONDecoder().decode([ScriptureSection].self, from: data) else {
|
|
// Fallback if JSON parsing fails
|
|
return [ScriptureSection(verse: text, reference: "")]
|
|
}
|
|
return sections
|
|
}
|
|
}
|
|
|
|
struct ScriptureSection: Codable {
|
|
let verse: String
|
|
let reference: String
|
|
}
|
|
|
|
// MARK: - Scripture Utilities
|
|
|
|
func extractScriptureReferences(from text: String) -> String {
|
|
// Use the Rust implementation for consistent parsing across platforms
|
|
return extractScriptureReferencesString(scriptureText: text)
|
|
}
|
|
|
|
// MARK: - Share Utilities
|
|
|
|
func createShareItems(for sermon: Sermon) -> [Any] {
|
|
// Use the Rust implementation for consistent share text across platforms
|
|
let jsonString = createSermonShareItemsJson(
|
|
title: sermon.title,
|
|
speaker: sermon.speaker,
|
|
videoUrl: sermon.videoUrl,
|
|
audioUrl: sermon.audioUrl
|
|
)
|
|
|
|
guard let data = jsonString.data(using: .utf8),
|
|
let shareStrings = try? JSONDecoder().decode([String].self, from: data) else {
|
|
// Fallback
|
|
return ["Check out this sermon: \"\(sermon.title)\" by \(sermon.speaker)"]
|
|
}
|
|
|
|
var items: [Any] = []
|
|
for shareString in shareStrings {
|
|
if let url = URL(string: shareString), shareString.hasPrefix("http") {
|
|
items.append(url)
|
|
} else {
|
|
items.append(shareString)
|
|
}
|
|
}
|
|
|
|
return items
|
|
}
|