Fix date formatting and time range display for sermons and livestreams
- Add shared date formatting function to eliminate DRY violations - Fix livestream cards showing raw ISO dates by using shared formatter - Update formatted_start_time() to return time range (start - end) for events - Switch events API to use v2 endpoint to avoid timezone conversion issues - Map duration_string to duration field for frontend compatibility
This commit is contained in:
parent
d83467939f
commit
dbcbf9626f
100
src/api.rs
100
src/api.rs
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
ChurchApiClient, ChurchCoreConfig,
|
||||
models::{NewSchedule, ScheduleUpdate, NewBulletin, BulletinUpdate, NewEvent, EventUpdate},
|
||||
utils::validation::{validate_event_form, validate_event_field, EventFormData, ValidationResult},
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
use std::sync::OnceLock;
|
||||
|
@ -141,7 +142,7 @@ pub fn fetch_events_json() -> String {
|
|||
let client = get_client();
|
||||
let rt = get_runtime();
|
||||
|
||||
match rt.block_on(client.get_upcoming_events(Some(50))) {
|
||||
match rt.block_on(client.get_upcoming_events_v2(Some(50))) {
|
||||
Ok(events) => {
|
||||
// Format events with display formatting using existing Event methods
|
||||
let formatted_events: Vec<_> = events.iter().map(|event| {
|
||||
|
@ -171,16 +172,69 @@ pub fn fetch_featured_events_json() -> String {
|
|||
}
|
||||
}
|
||||
|
||||
// Shared function to format sermon-like items with consistent date formatting
|
||||
fn format_sermon_items_with_dates<T>(items: Vec<T>) -> String
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
use serde_json::Value;
|
||||
|
||||
let formatted_items: Vec<_> = items.iter().map(|item| {
|
||||
let mut item_json = serde_json::to_value(item).unwrap_or_default();
|
||||
|
||||
// Format date and duration fields for frontend compatibility
|
||||
if let Some(obj) = item_json.as_object_mut() {
|
||||
// Handle date formatting
|
||||
if let Some(date_value) = obj.get("date") {
|
||||
// Try to parse as DateTime and format
|
||||
if let Some(date_str) = date_value.as_str() {
|
||||
// Try parsing ISO format first
|
||||
if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(date_str) {
|
||||
let formatted_date = datetime.format("%B %d, %Y").to_string();
|
||||
obj.insert("date".to_string(), Value::String(formatted_date));
|
||||
} else if let Ok(datetime) = chrono::DateTime::parse_from_str(&format!("{} 00:00:00 +0000", date_str), "%Y-%m-%d %H:%M:%S %z") {
|
||||
let formatted_date = datetime.format("%B %d, %Y").to_string();
|
||||
obj.insert("date".to_string(), Value::String(formatted_date));
|
||||
}
|
||||
} else if let Some(date_obj) = date_value.as_object() {
|
||||
// Handle DateTime objects from serde serialization
|
||||
if let (Some(secs), Some(nanos)) = (date_obj.get("secs_since_epoch"), date_obj.get("nanos_since_epoch")) {
|
||||
if let (Some(secs), Some(nanos)) = (secs.as_i64(), nanos.as_u64()) {
|
||||
if let Some(datetime) = chrono::DateTime::from_timestamp(secs, nanos as u32) {
|
||||
let formatted_date = datetime.format("%B %d, %Y").to_string();
|
||||
obj.insert("date".to_string(), Value::String(formatted_date));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map duration_string to duration for frontend compatibility
|
||||
if let Some(duration_string) = obj.get("duration_string").and_then(|v| v.as_str()) {
|
||||
obj.insert("duration".to_string(), Value::String(duration_string.to_string()));
|
||||
}
|
||||
}
|
||||
item_json
|
||||
}).collect();
|
||||
|
||||
serde_json::to_string(&formatted_items).unwrap_or_else(|_| "[]".to_string())
|
||||
}
|
||||
|
||||
pub fn fetch_sermons_json() -> String {
|
||||
let client = get_client();
|
||||
let rt = get_runtime();
|
||||
|
||||
match rt.block_on(client.get_recent_sermons(Some(20))) {
|
||||
Ok(sermons) => serde_json::to_string(&sermons).unwrap_or_else(|_| "[]".to_string()),
|
||||
Ok(sermons) => format_sermon_items_with_dates(sermons),
|
||||
Err(_) => "[]".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_sermons_from_json(sermons_json: String) -> String {
|
||||
// For compatibility with iOS - just pass through since we already format properly
|
||||
sermons_json
|
||||
}
|
||||
|
||||
pub fn fetch_config_json() -> String {
|
||||
let client = get_client();
|
||||
let rt = get_runtime();
|
||||
|
@ -191,6 +245,22 @@ pub fn fetch_config_json() -> String {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_config_json(config_json: String) -> String {
|
||||
let client = get_client();
|
||||
let rt = get_runtime();
|
||||
|
||||
// Parse the JSON into a ChurchConfig
|
||||
match serde_json::from_str(&config_json) {
|
||||
Ok(config) => {
|
||||
match rt.block_on(client.update_config(config)) {
|
||||
Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| r#"{"success": false, "error": "JSON serialization failed"}"#.to_string()),
|
||||
Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| r#"{"success": false, "error": "Unknown error"}"#.to_string()),
|
||||
}
|
||||
},
|
||||
Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| r#"{"success": false, "error": "JSON parsing failed"}"#.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_random_bible_verse_json() -> String {
|
||||
let client = get_client();
|
||||
let rt = get_runtime();
|
||||
|
@ -238,7 +308,7 @@ pub fn fetch_livestream_archive_json() -> String {
|
|||
let rt = get_runtime();
|
||||
|
||||
match rt.block_on(client.get_livestreams()) {
|
||||
Ok(streams) => serde_json::to_string(&streams).unwrap_or_else(|_| "[]".to_string()),
|
||||
Ok(streams) => format_sermon_items_with_dates(streams),
|
||||
Err(_) => "[]".to_string(),
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +333,30 @@ pub fn validate_contact_form_json(form_json: String) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn validate_event_form_json(event_json: String) -> String {
|
||||
match serde_json::from_str::<EventFormData>(&event_json) {
|
||||
Ok(event_data) => {
|
||||
let result = validate_event_form(&event_data);
|
||||
serde_json::to_string(&result).unwrap_or_else(|_| r#"{"is_valid": false, "errors": ["JSON serialization failed"]}"#.to_string())
|
||||
},
|
||||
Err(e) => {
|
||||
let result = ValidationResult::invalid(vec![format!("Invalid JSON format: {}", e)]);
|
||||
serde_json::to_string(&result).unwrap_or_else(|_| r#"{"is_valid": false, "errors": ["JSON parsing failed"]}"#.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_event_field_json(field_name: String, value: String, event_json: Option<String>) -> String {
|
||||
let event_data = if let Some(json) = event_json {
|
||||
serde_json::from_str::<EventFormData>(&json).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let result = validate_event_field(&field_name, &value, event_data.as_ref());
|
||||
serde_json::to_string(&result).unwrap_or_else(|_| r#"{"is_valid": false, "errors": ["JSON serialization failed"]}"#.to_string())
|
||||
}
|
||||
|
||||
pub fn submit_event_json(
|
||||
title: String,
|
||||
description: String,
|
||||
|
|
|
@ -262,9 +262,14 @@ impl Event {
|
|||
}
|
||||
|
||||
pub fn formatted_start_time(&self) -> String {
|
||||
// Convert UTC to user's local timezone automatically
|
||||
let local_time = self.start_time.with_timezone(&chrono::Local);
|
||||
local_time.format("%I:%M %p").to_string().trim_start_matches('0').to_string()
|
||||
// Return time range (start - end time) for frontend compatibility
|
||||
let start_local = self.start_time.with_timezone(&chrono::Local);
|
||||
let end_local = self.end_time.with_timezone(&chrono::Local);
|
||||
|
||||
let start_formatted = start_local.format("%I:%M %p").to_string().trim_start_matches('0').to_string();
|
||||
let end_formatted = end_local.format("%I:%M %p").to_string().trim_start_matches('0').to_string();
|
||||
|
||||
format!("{} - {}", start_formatted, end_formatted)
|
||||
}
|
||||
|
||||
pub fn formatted_end_time(&self) -> String {
|
||||
|
|
Loading…
Reference in a new issue