Files
calendar/calendar-models/src/vevent.rs
Connor Johnstone 79f287ed61
Some checks failed
Build and Push Docker Image / docker (push) Failing after 1m7s
Fix calendar event fetching to use visible date range
Moved event fetching logic from CalendarView to Calendar component to properly
use the visible date range instead of hardcoded current month. The Calendar
component already tracks the current visible date through navigation, so events
now load correctly for August and other months when navigating.

Changes:
- Calendar component now manages its own events state and fetching
- Event fetching responds to current_date changes from navigation
- CalendarView simplified to just render Calendar component
- Fixed cargo fmt/clippy formatting across codebase

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 18:31:51 -04:00

186 lines
6.5 KiB
Rust

//! VEvent - RFC 5545 compliant calendar event structure
use crate::common::*;
use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
// ==================== VEVENT COMPONENT ====================
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VEvent {
// Required properties
pub dtstamp: DateTime<Utc>, // Date-time stamp (DTSTAMP) - REQUIRED
pub uid: String, // Unique identifier (UID) - REQUIRED
pub dtstart: DateTime<Utc>, // Start date-time (DTSTART) - REQUIRED
// Optional properties (commonly used)
pub dtend: Option<DateTime<Utc>>, // End date-time (DTEND)
pub duration: Option<Duration>, // Duration (DURATION) - alternative to DTEND
pub summary: Option<String>, // Summary/title (SUMMARY)
pub description: Option<String>, // Description (DESCRIPTION)
pub location: Option<String>, // Location (LOCATION)
// Classification and status
pub class: Option<EventClass>, // Classification (CLASS)
pub status: Option<EventStatus>, // Status (STATUS)
pub transp: Option<TimeTransparency>, // Time transparency (TRANSP)
pub priority: Option<u8>, // Priority 0-9 (PRIORITY)
// People and organization
pub organizer: Option<CalendarUser>, // Organizer (ORGANIZER)
pub attendees: Vec<Attendee>, // Attendees (ATTENDEE)
pub contact: Option<String>, // Contact information (CONTACT)
// Categorization and relationships
pub categories: Vec<String>, // Categories (CATEGORIES)
pub comment: Option<String>, // Comment (COMMENT)
pub resources: Vec<String>, // Resources (RESOURCES)
pub related_to: Option<String>, // Related component (RELATED-TO)
pub url: Option<String>, // URL (URL)
// Geographical
pub geo: Option<GeographicPosition>, // Geographic position (GEO)
// Versioning and modification
pub sequence: Option<u32>, // Sequence number (SEQUENCE)
pub created: Option<DateTime<Utc>>, // Creation time (CREATED)
pub last_modified: Option<DateTime<Utc>>, // Last modified (LAST-MODIFIED)
// Recurrence
pub rrule: Option<String>, // Recurrence rule (RRULE)
pub rdate: Vec<DateTime<Utc>>, // Recurrence dates (RDATE)
pub exdate: Vec<DateTime<Utc>>, // Exception dates (EXDATE)
pub recurrence_id: Option<DateTime<Utc>>, // Recurrence ID (RECURRENCE-ID)
// Alarms and attachments
pub alarms: Vec<VAlarm>, // VALARM components
pub attachments: Vec<Attachment>, // Attachments (ATTACH)
// CalDAV specific (for implementation)
pub etag: Option<String>, // ETag for CalDAV
pub href: Option<String>, // Href for CalDAV
pub calendar_path: Option<String>, // Calendar path
pub all_day: bool, // All-day event flag
}
impl VEvent {
/// Create a new VEvent with required fields
pub fn new(uid: String, dtstart: DateTime<Utc>) -> Self {
Self {
dtstamp: Utc::now(),
uid,
dtstart,
dtend: None,
duration: None,
summary: None,
description: None,
location: None,
class: None,
status: None,
transp: None,
priority: None,
organizer: None,
attendees: Vec::new(),
contact: None,
categories: Vec::new(),
comment: None,
resources: Vec::new(),
related_to: None,
url: None,
geo: None,
sequence: None,
created: Some(Utc::now()),
last_modified: Some(Utc::now()),
rrule: None,
rdate: Vec::new(),
exdate: Vec::new(),
recurrence_id: None,
alarms: Vec::new(),
attachments: Vec::new(),
etag: None,
href: None,
calendar_path: None,
all_day: false,
}
}
/// Helper method to get effective end time (dtend or dtstart + duration)
pub fn get_end_time(&self) -> DateTime<Utc> {
if let Some(dtend) = self.dtend {
dtend
} else if let Some(duration) = self.duration {
self.dtstart + duration
} else {
// Default to 1 hour if no end or duration specified
self.dtstart + Duration::hours(1)
}
}
/// Helper method to get event duration
pub fn get_duration(&self) -> Duration {
if let Some(duration) = self.duration {
duration
} else if let Some(dtend) = self.dtend {
dtend - self.dtstart
} else {
Duration::hours(1) // Default duration
}
}
/// Helper method to get display title (summary or "Untitled Event")
pub fn get_title(&self) -> String {
self.summary
.clone()
.unwrap_or_else(|| "Untitled Event".to_string())
}
/// Helper method to get start date for UI compatibility
pub fn get_date(&self) -> chrono::NaiveDate {
self.dtstart.date_naive()
}
/// Check if event is recurring
pub fn is_recurring(&self) -> bool {
self.rrule.is_some()
}
/// Check if this is an exception to a recurring series
pub fn is_exception(&self) -> bool {
self.recurrence_id.is_some()
}
/// Get display string for status
pub fn get_status_display(&self) -> &'static str {
match &self.status {
Some(EventStatus::Tentative) => "Tentative",
Some(EventStatus::Confirmed) => "Confirmed",
Some(EventStatus::Cancelled) => "Cancelled",
None => "Confirmed", // Default
}
}
/// Get display string for class
pub fn get_class_display(&self) -> &'static str {
match &self.class {
Some(EventClass::Public) => "Public",
Some(EventClass::Private) => "Private",
Some(EventClass::Confidential) => "Confidential",
None => "Public", // Default
}
}
/// Get display string for priority
pub fn get_priority_display(&self) -> String {
match self.priority {
None => "Not set".to_string(),
Some(0) => "Undefined".to_string(),
Some(1) => "High".to_string(),
Some(p) if p <= 4 => "High".to_string(),
Some(5) => "Medium".to_string(),
Some(p) if p <= 8 => "Low".to_string(),
Some(9) => "Low".to_string(),
Some(p) => format!("Priority {}", p),
}
}
}