Complete frontend migration to shared VEvent model
- Update frontend to use shared calendar-models library - Add display methods to VEvent for UI compatibility - Remove duplicate VEvent definitions in frontend - Fix JSON field name mismatch (dtstart/dtend vs start/end) - Create CalendarEvent type alias for backward compatibility - Resolve "missing field start" parsing error 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -146,4 +146,38 @@ impl VEvent {
|
|||||||
pub fn is_exception(&self) -> bool {
|
pub fn is_exception(&self) -> bool {
|
||||||
self.recurrence_id.is_some()
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ pub fn calendar_view(props: &CalendarViewProps) -> Html {
|
|||||||
|
|
||||||
match calendar_service.refresh_event(&token, &password, &uid).await {
|
match calendar_service.refresh_event(&token, &password, &uid).await {
|
||||||
Ok(Some(refreshed_event)) => {
|
Ok(Some(refreshed_event)) => {
|
||||||
let refreshed_vevent = VEvent::from_calendar_event(&refreshed_event);
|
let refreshed_vevent = refreshed_event; // CalendarEvent is now VEvent
|
||||||
let mut updated_events = (*events).clone();
|
let mut updated_events = (*events).clone();
|
||||||
|
|
||||||
for (_, day_events) in updated_events.iter_mut() {
|
for (_, day_events) in updated_events.iter_mut() {
|
||||||
|
|||||||
@@ -1,841 +1,2 @@
|
|||||||
// RFC 5545 Compliant iCalendar Data Structures
|
// Re-export from shared calendar-models library for backward compatibility
|
||||||
// This file contains updated structures that fully comply with RFC 5545 iCalendar specification
|
pub use calendar_models::*;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc, Duration};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
// ==================== CALENDAR OBJECT (VCALENDAR) ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct ICalendarObject {
|
|
||||||
// Required calendar properties
|
|
||||||
pub prodid: String, // Product identifier (PRODID)
|
|
||||||
pub version: String, // Version (typically "2.0")
|
|
||||||
|
|
||||||
// Optional calendar properties
|
|
||||||
pub calscale: Option<String>, // Calendar scale (CALSCALE) - default "GREGORIAN"
|
|
||||||
pub method: Option<String>, // Method (METHOD)
|
|
||||||
|
|
||||||
// Components
|
|
||||||
pub events: Vec<VEvent>, // VEVENT components
|
|
||||||
pub todos: Vec<VTodo>, // VTODO components
|
|
||||||
pub journals: Vec<VJournal>, // VJOURNAL components
|
|
||||||
pub freebusys: Vec<VFreeBusy>, // VFREEBUSY components
|
|
||||||
pub timezones: Vec<VTimeZone>, // VTIMEZONE components
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== VTODO COMPONENT ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct VTodo {
|
|
||||||
// Required properties
|
|
||||||
pub dtstamp: DateTime<Utc>, // Date-time stamp (DTSTAMP) - REQUIRED
|
|
||||||
pub uid: String, // Unique identifier (UID) - REQUIRED
|
|
||||||
|
|
||||||
// Optional date-time properties
|
|
||||||
pub dtstart: Option<DateTime<Utc>>, // Start date-time (DTSTART)
|
|
||||||
pub due: Option<DateTime<Utc>>, // Due date-time (DUE)
|
|
||||||
pub duration: Option<Duration>, // Duration (DURATION)
|
|
||||||
pub completed: Option<DateTime<Utc>>, // Completion date-time (COMPLETED)
|
|
||||||
|
|
||||||
// Descriptive properties
|
|
||||||
pub summary: Option<String>, // Summary/title (SUMMARY)
|
|
||||||
pub description: Option<String>, // Description (DESCRIPTION)
|
|
||||||
pub location: Option<String>, // Location (LOCATION)
|
|
||||||
|
|
||||||
// Status and completion
|
|
||||||
pub status: Option<TodoStatus>, // Status (STATUS)
|
|
||||||
pub percent_complete: Option<u8>, // Percent complete 0-100 (PERCENT-COMPLETE)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== VJOURNAL COMPONENT ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct VJournal {
|
|
||||||
// Required properties
|
|
||||||
pub dtstamp: DateTime<Utc>, // Date-time stamp (DTSTAMP) - REQUIRED
|
|
||||||
pub uid: String, // Unique identifier (UID) - REQUIRED
|
|
||||||
|
|
||||||
// Optional properties
|
|
||||||
pub dtstart: Option<DateTime<Utc>>, // Start date-time (DTSTART)
|
|
||||||
pub summary: Option<String>, // Summary/title (SUMMARY)
|
|
||||||
pub description: Option<String>, // Description (DESCRIPTION)
|
|
||||||
|
|
||||||
// Classification and status
|
|
||||||
pub class: Option<EventClass>, // Classification (CLASS)
|
|
||||||
pub status: Option<JournalStatus>, // Status (STATUS)
|
|
||||||
|
|
||||||
// People and organization
|
|
||||||
pub organizer: Option<CalendarUser>, // Organizer (ORGANIZER)
|
|
||||||
pub contact: Option<String>, // Contact information (CONTACT)
|
|
||||||
|
|
||||||
// Categorization and relationships
|
|
||||||
pub categories: Vec<String>, // Categories (CATEGORIES)
|
|
||||||
pub comment: Option<String>, // Comment (COMMENT)
|
|
||||||
pub related_to: Option<String>, // Related component (RELATED-TO)
|
|
||||||
pub url: Option<String>, // URL (URL)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// Attachments
|
|
||||||
pub attachments: Vec<Attachment>, // Attachments (ATTACH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== VFREEBUSY COMPONENT ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct VFreeBusy {
|
|
||||||
// Required properties
|
|
||||||
pub dtstamp: DateTime<Utc>, // Date-time stamp (DTSTAMP) - REQUIRED
|
|
||||||
pub uid: String, // Unique identifier (UID) - REQUIRED
|
|
||||||
|
|
||||||
// Optional properties
|
|
||||||
pub dtstart: Option<DateTime<Utc>>, // Start date-time (DTSTART)
|
|
||||||
pub dtend: Option<DateTime<Utc>>, // End date-time (DTEND)
|
|
||||||
pub duration: Option<Duration>, // Duration (DURATION)
|
|
||||||
|
|
||||||
// Free/busy information
|
|
||||||
pub freebusy: Vec<FreeBusyTime>, // Free/busy periods (FREEBUSY)
|
|
||||||
|
|
||||||
// People and organization
|
|
||||||
pub organizer: Option<CalendarUser>, // Organizer (ORGANIZER)
|
|
||||||
pub attendees: Vec<CalendarUser>, // Attendees (ATTENDEE)
|
|
||||||
pub contact: Option<String>, // Contact information (CONTACT)
|
|
||||||
|
|
||||||
// Additional properties
|
|
||||||
pub comment: Option<String>, // Comment (COMMENT)
|
|
||||||
pub url: Option<String>, // URL (URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== VTIMEZONE COMPONENT ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct VTimeZone {
|
|
||||||
// Required properties
|
|
||||||
pub tzid: String, // Time zone identifier (TZID) - REQUIRED
|
|
||||||
|
|
||||||
// Optional properties
|
|
||||||
pub tzname: Option<String>, // Time zone name (TZNAME)
|
|
||||||
pub tzurl: Option<String>, // Time zone URL (TZURL)
|
|
||||||
|
|
||||||
// Standard and daylight components
|
|
||||||
pub standard: Vec<TimeZoneComponent>, // Standard time components
|
|
||||||
pub daylight: Vec<TimeZoneComponent>, // Daylight time components
|
|
||||||
|
|
||||||
// Last modified
|
|
||||||
pub last_modified: Option<DateTime<Utc>>, // Last modified (LAST-MODIFIED)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct TimeZoneComponent {
|
|
||||||
pub dtstart: DateTime<Utc>, // Start date-time (DTSTART) - REQUIRED
|
|
||||||
pub tzoffsetfrom: String, // UTC offset from (TZOFFSETFROM) - REQUIRED
|
|
||||||
pub tzoffsetto: String, // UTC offset to (TZOFFSETTO) - REQUIRED
|
|
||||||
|
|
||||||
pub tzname: Option<String>, // Time zone name (TZNAME)
|
|
||||||
pub comment: Option<String>, // Comment (COMMENT)
|
|
||||||
|
|
||||||
// Recurrence
|
|
||||||
pub rrule: Option<String>, // Recurrence rule (RRULE)
|
|
||||||
pub rdate: Vec<DateTime<Utc>>, // Recurrence dates (RDATE)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== VALARM COMPONENT ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct VAlarm {
|
|
||||||
pub action: AlarmAction, // Action (ACTION) - REQUIRED
|
|
||||||
pub trigger: AlarmTrigger, // Trigger (TRIGGER) - REQUIRED
|
|
||||||
|
|
||||||
// Optional properties (some required based on action)
|
|
||||||
pub description: Option<String>, // Description (DESCRIPTION)
|
|
||||||
pub summary: Option<String>, // Summary (SUMMARY)
|
|
||||||
pub duration: Option<Duration>, // Duration (DURATION)
|
|
||||||
pub repeat: Option<u32>, // Repeat count (REPEAT)
|
|
||||||
pub attendees: Vec<CalendarUser>, // Attendees (ATTENDEE) - for EMAIL action
|
|
||||||
pub attachments: Vec<Attachment>, // Attachments (ATTACH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== SUPPORTING TYPES ====================
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum EventClass {
|
|
||||||
Public,
|
|
||||||
Private,
|
|
||||||
Confidential,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum EventStatus {
|
|
||||||
Tentative,
|
|
||||||
Confirmed,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum TodoStatus {
|
|
||||||
NeedsAction,
|
|
||||||
Completed,
|
|
||||||
InProcess,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum JournalStatus {
|
|
||||||
Draft,
|
|
||||||
Final,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum TimeTransparency {
|
|
||||||
Opaque, // Time is not available (default)
|
|
||||||
Transparent, // Time is available despite event
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum AlarmAction {
|
|
||||||
Audio,
|
|
||||||
Display,
|
|
||||||
Email,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum AlarmTrigger {
|
|
||||||
Duration(Duration), // Relative to start/end
|
|
||||||
DateTime(DateTime<Utc>), // Absolute time
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct CalendarUser {
|
|
||||||
pub cal_address: String, // Calendar address (email)
|
|
||||||
pub cn: Option<String>, // Common name (CN parameter)
|
|
||||||
pub dir: Option<String>, // Directory entry (DIR parameter)
|
|
||||||
pub sent_by: Option<String>, // Sent by (SENT-BY parameter)
|
|
||||||
pub language: Option<String>, // Language (LANGUAGE parameter)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct Attendee {
|
|
||||||
pub cal_address: String, // Calendar address (email)
|
|
||||||
pub cn: Option<String>, // Common name (CN parameter)
|
|
||||||
pub role: Option<AttendeeRole>, // Role (ROLE parameter)
|
|
||||||
pub partstat: Option<ParticipationStatus>, // Participation status (PARTSTAT parameter)
|
|
||||||
pub rsvp: Option<bool>, // RSVP expectation (RSVP parameter)
|
|
||||||
pub cutype: Option<CalendarUserType>, // Calendar user type (CUTYPE parameter)
|
|
||||||
pub member: Vec<String>, // Group/list membership (MEMBER parameter)
|
|
||||||
pub delegated_to: Vec<String>, // Delegated to (DELEGATED-TO parameter)
|
|
||||||
pub delegated_from: Vec<String>, // Delegated from (DELEGATED-FROM parameter)
|
|
||||||
pub sent_by: Option<String>, // Sent by (SENT-BY parameter)
|
|
||||||
pub dir: Option<String>, // Directory entry (DIR parameter)
|
|
||||||
pub language: Option<String>, // Language (LANGUAGE parameter)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum AttendeeRole {
|
|
||||||
Chair,
|
|
||||||
ReqParticipant,
|
|
||||||
OptParticipant,
|
|
||||||
NonParticipant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum ParticipationStatus {
|
|
||||||
NeedsAction,
|
|
||||||
Accepted,
|
|
||||||
Declined,
|
|
||||||
Tentative,
|
|
||||||
Delegated,
|
|
||||||
Completed,
|
|
||||||
InProcess,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum CalendarUserType {
|
|
||||||
Individual,
|
|
||||||
Group,
|
|
||||||
Resource,
|
|
||||||
Room,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct GeographicPosition {
|
|
||||||
pub latitude: f64, // Latitude in decimal degrees
|
|
||||||
pub longitude: f64, // Longitude in decimal degrees
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct Attachment {
|
|
||||||
pub data: AttachmentData, // Attachment data
|
|
||||||
pub fmttype: Option<String>, // Format type (FMTTYPE parameter)
|
|
||||||
pub encoding: Option<String>, // Encoding (ENCODING parameter)
|
|
||||||
pub filename: Option<String>, // Filename (X-FILENAME parameter)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum AttachmentData {
|
|
||||||
Uri(String), // URI reference
|
|
||||||
Binary(Vec<u8>), // Binary data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct FreeBusyTime {
|
|
||||||
pub period: (DateTime<Utc>, DateTime<Utc>), // Start and end time
|
|
||||||
pub fbtype: Option<FreeBusyType>, // Free/busy type
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum FreeBusyType {
|
|
||||||
Free,
|
|
||||||
Busy,
|
|
||||||
BusyUnavailable,
|
|
||||||
BusyTentative,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== COMPATIBILITY LAYER ====================
|
|
||||||
|
|
||||||
use crate::services::calendar_service::{CalendarEvent, EventReminder, ReminderAction};
|
|
||||||
|
|
||||||
// Conversion from new VEvent to existing CalendarEvent
|
|
||||||
impl From<VEvent> for CalendarEvent {
|
|
||||||
fn from(vevent: VEvent) -> Self {
|
|
||||||
Self {
|
|
||||||
uid: vevent.uid,
|
|
||||||
summary: vevent.summary,
|
|
||||||
description: vevent.description,
|
|
||||||
start: vevent.dtstart,
|
|
||||||
end: vevent.dtend,
|
|
||||||
location: vevent.location,
|
|
||||||
status: vevent.status.unwrap_or(EventStatus::Confirmed).into(),
|
|
||||||
class: vevent.class.unwrap_or(EventClass::Public).into(),
|
|
||||||
priority: vevent.priority,
|
|
||||||
organizer: vevent.organizer.map(|o| o.cal_address),
|
|
||||||
attendees: vevent.attendees.into_iter().map(|a| a.cal_address).collect(),
|
|
||||||
categories: vevent.categories,
|
|
||||||
created: vevent.created,
|
|
||||||
last_modified: vevent.last_modified,
|
|
||||||
recurrence_rule: vevent.rrule,
|
|
||||||
exception_dates: vevent.exdate,
|
|
||||||
all_day: vevent.all_day,
|
|
||||||
reminders: vevent.alarms.into_iter().map(|a| a.into()).collect(),
|
|
||||||
etag: vevent.etag,
|
|
||||||
href: vevent.href,
|
|
||||||
calendar_path: vevent.calendar_path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conversion from existing CalendarEvent to new VEvent
|
|
||||||
impl From<CalendarEvent> for VEvent {
|
|
||||||
fn from(event: CalendarEvent) -> Self {
|
|
||||||
use chrono::Utc;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
// Required properties
|
|
||||||
dtstamp: Utc::now(), // Add required DTSTAMP
|
|
||||||
uid: event.uid,
|
|
||||||
dtstart: event.start,
|
|
||||||
|
|
||||||
// Optional properties
|
|
||||||
dtend: event.end,
|
|
||||||
duration: None, // Will be calculated from dtend if needed
|
|
||||||
summary: event.summary,
|
|
||||||
description: event.description,
|
|
||||||
location: event.location,
|
|
||||||
|
|
||||||
// Classification and status
|
|
||||||
class: Some(event.class.into()),
|
|
||||||
status: Some(event.status.into()),
|
|
||||||
transp: None, // Default to None, can be enhanced later
|
|
||||||
priority: event.priority,
|
|
||||||
|
|
||||||
// People and organization
|
|
||||||
organizer: event.organizer.map(|email| CalendarUser {
|
|
||||||
cal_address: email,
|
|
||||||
cn: None,
|
|
||||||
dir: None,
|
|
||||||
sent_by: None,
|
|
||||||
language: None,
|
|
||||||
}),
|
|
||||||
attendees: event.attendees.into_iter().map(|email| Attendee {
|
|
||||||
cal_address: email,
|
|
||||||
cn: None,
|
|
||||||
role: None,
|
|
||||||
partstat: None,
|
|
||||||
rsvp: None,
|
|
||||||
cutype: None,
|
|
||||||
member: Vec::new(),
|
|
||||||
delegated_to: Vec::new(),
|
|
||||||
delegated_from: Vec::new(),
|
|
||||||
sent_by: None,
|
|
||||||
dir: None,
|
|
||||||
language: None,
|
|
||||||
}).collect(),
|
|
||||||
contact: None,
|
|
||||||
|
|
||||||
// Categorization and relationships
|
|
||||||
categories: event.categories,
|
|
||||||
comment: None,
|
|
||||||
resources: Vec::new(),
|
|
||||||
related_to: None,
|
|
||||||
url: None,
|
|
||||||
|
|
||||||
// Geographical
|
|
||||||
geo: None,
|
|
||||||
|
|
||||||
// Versioning and modification
|
|
||||||
sequence: Some(0), // Start with sequence 0
|
|
||||||
created: event.created,
|
|
||||||
last_modified: event.last_modified,
|
|
||||||
|
|
||||||
// Recurrence
|
|
||||||
rrule: event.recurrence_rule,
|
|
||||||
rdate: Vec::new(),
|
|
||||||
exdate: event.exception_dates,
|
|
||||||
recurrence_id: None,
|
|
||||||
|
|
||||||
// Alarms and attachments
|
|
||||||
alarms: event.reminders.into_iter().map(|r| r.into()).collect(),
|
|
||||||
attachments: Vec::new(),
|
|
||||||
|
|
||||||
// CalDAV specific
|
|
||||||
etag: event.etag,
|
|
||||||
href: event.href,
|
|
||||||
calendar_path: event.calendar_path,
|
|
||||||
all_day: event.all_day,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between status enums
|
|
||||||
impl From<EventStatus> for crate::services::calendar_service::EventStatus {
|
|
||||||
fn from(status: EventStatus) -> Self {
|
|
||||||
match status {
|
|
||||||
EventStatus::Tentative => crate::services::calendar_service::EventStatus::Tentative,
|
|
||||||
EventStatus::Confirmed => crate::services::calendar_service::EventStatus::Confirmed,
|
|
||||||
EventStatus::Cancelled => crate::services::calendar_service::EventStatus::Cancelled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<crate::services::calendar_service::EventStatus> for EventStatus {
|
|
||||||
fn from(status: crate::services::calendar_service::EventStatus) -> Self {
|
|
||||||
match status {
|
|
||||||
crate::services::calendar_service::EventStatus::Tentative => EventStatus::Tentative,
|
|
||||||
crate::services::calendar_service::EventStatus::Confirmed => EventStatus::Confirmed,
|
|
||||||
crate::services::calendar_service::EventStatus::Cancelled => EventStatus::Cancelled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between class enums
|
|
||||||
impl From<EventClass> for crate::services::calendar_service::EventClass {
|
|
||||||
fn from(class: EventClass) -> Self {
|
|
||||||
match class {
|
|
||||||
EventClass::Public => crate::services::calendar_service::EventClass::Public,
|
|
||||||
EventClass::Private => crate::services::calendar_service::EventClass::Private,
|
|
||||||
EventClass::Confidential => crate::services::calendar_service::EventClass::Confidential,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<crate::services::calendar_service::EventClass> for EventClass {
|
|
||||||
fn from(class: crate::services::calendar_service::EventClass) -> Self {
|
|
||||||
match class {
|
|
||||||
crate::services::calendar_service::EventClass::Public => EventClass::Public,
|
|
||||||
crate::services::calendar_service::EventClass::Private => EventClass::Private,
|
|
||||||
crate::services::calendar_service::EventClass::Confidential => EventClass::Confidential,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between reminder types
|
|
||||||
impl From<VAlarm> for EventReminder {
|
|
||||||
fn from(alarm: VAlarm) -> Self {
|
|
||||||
let minutes_before = match alarm.trigger {
|
|
||||||
AlarmTrigger::Duration(duration) => {
|
|
||||||
// Convert duration to minutes (assuming it's negative for "before")
|
|
||||||
(-duration.num_minutes()) as i32
|
|
||||||
},
|
|
||||||
AlarmTrigger::DateTime(_) => 0, // Absolute time alarms default to 0 minutes
|
|
||||||
};
|
|
||||||
|
|
||||||
let action = match alarm.action {
|
|
||||||
AlarmAction::Display => ReminderAction::Display,
|
|
||||||
AlarmAction::Audio => ReminderAction::Audio,
|
|
||||||
AlarmAction::Email => ReminderAction::Email,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
minutes_before,
|
|
||||||
action,
|
|
||||||
description: alarm.description,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EventReminder> for VAlarm {
|
|
||||||
fn from(reminder: EventReminder) -> Self {
|
|
||||||
use chrono::Duration;
|
|
||||||
|
|
||||||
let action = match reminder.action {
|
|
||||||
ReminderAction::Display => AlarmAction::Display,
|
|
||||||
ReminderAction::Audio => AlarmAction::Audio,
|
|
||||||
ReminderAction::Email => AlarmAction::Email,
|
|
||||||
};
|
|
||||||
|
|
||||||
let trigger = AlarmTrigger::Duration(Duration::minutes(-reminder.minutes_before as i64));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
action,
|
|
||||||
trigger,
|
|
||||||
description: reminder.description,
|
|
||||||
summary: None,
|
|
||||||
duration: None,
|
|
||||||
repeat: None,
|
|
||||||
attendees: Vec::new(),
|
|
||||||
attachments: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== CONVERSION FUNCTIONS ====================
|
|
||||||
|
|
||||||
impl VEvent {
|
|
||||||
/// Convert from legacy CalendarEvent to RFC 5545 compliant VEvent
|
|
||||||
pub fn from_calendar_event(event: &crate::services::calendar_service::CalendarEvent) -> Self {
|
|
||||||
let now = chrono::Utc::now();
|
|
||||||
|
|
||||||
// Convert attendees from strings to structured Attendee objects
|
|
||||||
let attendees = event.attendees.iter()
|
|
||||||
.map(|email| Attendee {
|
|
||||||
cal_address: email.clone(),
|
|
||||||
cn: None,
|
|
||||||
role: Some(AttendeeRole::ReqParticipant),
|
|
||||||
partstat: Some(ParticipationStatus::NeedsAction),
|
|
||||||
rsvp: Some(true),
|
|
||||||
cutype: Some(CalendarUserType::Individual),
|
|
||||||
member: Vec::new(),
|
|
||||||
delegated_to: Vec::new(),
|
|
||||||
delegated_from: Vec::new(),
|
|
||||||
sent_by: None,
|
|
||||||
dir: None,
|
|
||||||
language: None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Convert reminders to alarms
|
|
||||||
let alarms = event.reminders.iter()
|
|
||||||
.map(|reminder| {
|
|
||||||
let action = match reminder.action {
|
|
||||||
crate::services::calendar_service::ReminderAction::Display => AlarmAction::Display,
|
|
||||||
crate::services::calendar_service::ReminderAction::Audio => AlarmAction::Audio,
|
|
||||||
crate::services::calendar_service::ReminderAction::Email => AlarmAction::Email,
|
|
||||||
};
|
|
||||||
|
|
||||||
VAlarm {
|
|
||||||
action,
|
|
||||||
trigger: AlarmTrigger::Duration(chrono::Duration::minutes(-reminder.minutes_before as i64)),
|
|
||||||
description: reminder.description.clone(),
|
|
||||||
summary: None,
|
|
||||||
duration: None,
|
|
||||||
repeat: None,
|
|
||||||
attendees: Vec::new(),
|
|
||||||
attachments: Vec::new(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Convert status
|
|
||||||
let status = match event.status {
|
|
||||||
crate::services::calendar_service::EventStatus::Tentative => Some(EventStatus::Tentative),
|
|
||||||
crate::services::calendar_service::EventStatus::Confirmed => Some(EventStatus::Confirmed),
|
|
||||||
crate::services::calendar_service::EventStatus::Cancelled => Some(EventStatus::Cancelled),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert class
|
|
||||||
let class = match event.class {
|
|
||||||
crate::services::calendar_service::EventClass::Public => Some(EventClass::Public),
|
|
||||||
crate::services::calendar_service::EventClass::Private => Some(EventClass::Private),
|
|
||||||
crate::services::calendar_service::EventClass::Confidential => Some(EventClass::Confidential),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
// Required properties
|
|
||||||
dtstamp: event.last_modified.unwrap_or(now),
|
|
||||||
uid: event.uid.clone(),
|
|
||||||
dtstart: event.start,
|
|
||||||
|
|
||||||
// Date/time properties
|
|
||||||
dtend: event.end,
|
|
||||||
duration: None, // Will use dtend instead
|
|
||||||
|
|
||||||
// Content properties
|
|
||||||
summary: event.summary.clone(),
|
|
||||||
description: event.description.clone(),
|
|
||||||
location: event.location.clone(),
|
|
||||||
|
|
||||||
// Classification and status
|
|
||||||
class,
|
|
||||||
status,
|
|
||||||
transp: None, // Default transparency
|
|
||||||
priority: event.priority,
|
|
||||||
|
|
||||||
// People and organization
|
|
||||||
organizer: event.organizer.as_ref().map(|org| CalendarUser {
|
|
||||||
cal_address: org.clone(),
|
|
||||||
cn: None,
|
|
||||||
dir: None,
|
|
||||||
sent_by: None,
|
|
||||||
language: None,
|
|
||||||
}),
|
|
||||||
attendees,
|
|
||||||
contact: None,
|
|
||||||
|
|
||||||
// Categorization
|
|
||||||
categories: event.categories.clone(),
|
|
||||||
comment: None,
|
|
||||||
resources: Vec::new(),
|
|
||||||
related_to: None,
|
|
||||||
url: None,
|
|
||||||
|
|
||||||
// Geographic
|
|
||||||
geo: None,
|
|
||||||
|
|
||||||
// Versioning
|
|
||||||
sequence: Some(0), // Start with sequence 0
|
|
||||||
created: event.created,
|
|
||||||
last_modified: event.last_modified,
|
|
||||||
|
|
||||||
// Recurrence
|
|
||||||
rrule: event.recurrence_rule.clone(),
|
|
||||||
rdate: Vec::new(),
|
|
||||||
exdate: event.exception_dates.clone(),
|
|
||||||
recurrence_id: None,
|
|
||||||
|
|
||||||
// Alarms and attachments
|
|
||||||
alarms,
|
|
||||||
attachments: Vec::new(),
|
|
||||||
|
|
||||||
// CalDAV specific
|
|
||||||
etag: event.etag.clone(),
|
|
||||||
href: event.href.clone(),
|
|
||||||
calendar_path: event.calendar_path.clone(),
|
|
||||||
all_day: event.all_day,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to legacy CalendarEvent for backward compatibility
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn to_calendar_event(&self) -> crate::services::calendar_service::CalendarEvent {
|
|
||||||
// Convert attendees back to simple email strings
|
|
||||||
let attendees = self.attendees.iter()
|
|
||||||
.map(|attendee| attendee.cal_address.clone())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Convert alarms back to simple reminders
|
|
||||||
let reminders = self.alarms.iter()
|
|
||||||
.filter_map(|alarm| {
|
|
||||||
if let AlarmTrigger::Duration(duration) = &alarm.trigger {
|
|
||||||
Some(crate::services::calendar_service::EventReminder {
|
|
||||||
minutes_before: (-duration.num_minutes()) as i32,
|
|
||||||
action: match alarm.action {
|
|
||||||
AlarmAction::Display => crate::services::calendar_service::ReminderAction::Display,
|
|
||||||
AlarmAction::Audio => crate::services::calendar_service::ReminderAction::Audio,
|
|
||||||
AlarmAction::Email => crate::services::calendar_service::ReminderAction::Email,
|
|
||||||
},
|
|
||||||
description: alarm.description.clone(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Convert status
|
|
||||||
let status = match self.status {
|
|
||||||
Some(EventStatus::Tentative) => crate::services::calendar_service::EventStatus::Tentative,
|
|
||||||
Some(EventStatus::Cancelled) => crate::services::calendar_service::EventStatus::Cancelled,
|
|
||||||
_ => crate::services::calendar_service::EventStatus::Confirmed,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert class
|
|
||||||
let class = match self.class {
|
|
||||||
Some(EventClass::Private) => crate::services::calendar_service::EventClass::Private,
|
|
||||||
Some(EventClass::Confidential) => crate::services::calendar_service::EventClass::Confidential,
|
|
||||||
_ => crate::services::calendar_service::EventClass::Public,
|
|
||||||
};
|
|
||||||
|
|
||||||
crate::services::calendar_service::CalendarEvent {
|
|
||||||
uid: self.uid.clone(),
|
|
||||||
summary: self.summary.clone(),
|
|
||||||
description: self.description.clone(),
|
|
||||||
start: self.dtstart,
|
|
||||||
end: self.dtend,
|
|
||||||
location: self.location.clone(),
|
|
||||||
status,
|
|
||||||
class,
|
|
||||||
priority: self.priority,
|
|
||||||
organizer: self.organizer.as_ref().map(|org| org.cal_address.clone()),
|
|
||||||
attendees,
|
|
||||||
categories: self.categories.clone(),
|
|
||||||
created: self.created,
|
|
||||||
last_modified: self.last_modified,
|
|
||||||
recurrence_rule: self.rrule.clone(),
|
|
||||||
exception_dates: self.exdate.clone(),
|
|
||||||
all_day: self.all_day,
|
|
||||||
reminders,
|
|
||||||
etag: self.etag.clone(),
|
|
||||||
href: self.href.clone(),
|
|
||||||
calendar_path: self.calendar_path.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the date for this event (for calendar display)
|
|
||||||
pub fn get_date(&self) -> chrono::NaiveDate {
|
|
||||||
if self.all_day {
|
|
||||||
self.dtstart.date_naive()
|
|
||||||
} else {
|
|
||||||
self.dtstart.date_naive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get display title for the event
|
|
||||||
pub fn get_title(&self) -> String {
|
|
||||||
self.summary.clone().unwrap_or_else(|| "Untitled Event".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,11 @@ use wasm_bindgen_futures::JsFuture;
|
|||||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Import RFC 5545 compliant VEvent
|
// Import RFC 5545 compliant VEvent from shared library
|
||||||
use crate::models::ical::VEvent;
|
use calendar_models::VEvent;
|
||||||
|
|
||||||
// Create type alias for smooth transition
|
// Create type alias for backward compatibility
|
||||||
|
pub type CalendarEvent = VEvent;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct EventReminder {
|
pub struct EventReminder {
|
||||||
@@ -44,56 +45,7 @@ pub struct CalendarInfo {
|
|||||||
pub color: String,
|
pub color: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
// CalendarEvent, EventStatus, and EventClass are now imported from shared library
|
||||||
pub struct CalendarEvent {
|
|
||||||
pub uid: String,
|
|
||||||
pub summary: Option<String>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub start: DateTime<Utc>,
|
|
||||||
pub end: Option<DateTime<Utc>>,
|
|
||||||
pub location: Option<String>,
|
|
||||||
pub status: EventStatus,
|
|
||||||
pub class: EventClass,
|
|
||||||
pub priority: Option<u8>,
|
|
||||||
pub organizer: Option<String>,
|
|
||||||
pub attendees: Vec<String>,
|
|
||||||
pub categories: Vec<String>,
|
|
||||||
pub created: Option<DateTime<Utc>>,
|
|
||||||
pub last_modified: Option<DateTime<Utc>>,
|
|
||||||
pub recurrence_rule: Option<String>,
|
|
||||||
pub exception_dates: Vec<DateTime<Utc>>,
|
|
||||||
pub all_day: bool,
|
|
||||||
pub reminders: Vec<EventReminder>,
|
|
||||||
pub etag: Option<String>,
|
|
||||||
pub href: Option<String>,
|
|
||||||
pub calendar_path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum EventStatus {
|
|
||||||
Tentative,
|
|
||||||
Confirmed,
|
|
||||||
Cancelled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EventStatus {
|
|
||||||
fn default() -> Self {
|
|
||||||
EventStatus::Confirmed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub enum EventClass {
|
|
||||||
Public,
|
|
||||||
Private,
|
|
||||||
Confidential,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EventClass {
|
|
||||||
fn default() -> Self {
|
|
||||||
EventClass::Public
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== V2 API MODELS ====================
|
// ==================== V2 API MODELS ====================
|
||||||
|
|
||||||
@@ -228,15 +180,8 @@ impl CalendarService {
|
|||||||
year: i32,
|
year: i32,
|
||||||
month: u32
|
month: u32
|
||||||
) -> Result<Vec<VEvent>, String> {
|
) -> Result<Vec<VEvent>, String> {
|
||||||
// Fetch CalendarEvents first
|
// Since CalendarEvent is now a type alias for VEvent, just call the main method
|
||||||
let calendar_events = self.fetch_events_for_month(token, password, year, month).await?;
|
self.fetch_events_for_month(token, password, year, month).await
|
||||||
|
|
||||||
// Convert to VEvent
|
|
||||||
let vevents = calendar_events.into_iter()
|
|
||||||
.map(|event| VEvent::from_calendar_event(&event))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(vevents)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_events_for_month(
|
pub async fn fetch_events_for_month(
|
||||||
|
|||||||
Reference in New Issue
Block a user