Added support for external calendars #14
@@ -93,6 +93,7 @@ pub async fn get_user_info(
|
||||
path: path.clone(),
|
||||
display_name: extract_calendar_name(path),
|
||||
color: generate_calendar_color(path),
|
||||
is_visible: true, // Default to visible
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ pub struct CalendarInfo {
|
||||
pub path: String,
|
||||
pub display_name: String,
|
||||
pub color: String,
|
||||
pub is_visible: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
@@ -1067,6 +1067,21 @@ pub fn App() -> Html {
|
||||
on_color_picker_toggle={on_color_picker_toggle}
|
||||
available_colors={(*available_colors).clone()}
|
||||
on_calendar_context_menu={on_calendar_context_menu}
|
||||
on_calendar_visibility_toggle={Callback::from({
|
||||
let user_info = user_info.clone();
|
||||
move |calendar_path: String| {
|
||||
let user_info = user_info.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
if let Some(mut info) = (*user_info).clone() {
|
||||
// Toggle the visibility
|
||||
if let Some(calendar) = info.calendars.iter_mut().find(|c| c.path == calendar_path) {
|
||||
calendar.is_visible = !calendar.is_visible;
|
||||
user_info.set(Some(info));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})}
|
||||
current_view={(*current_view).clone()}
|
||||
on_view_change={on_view_change}
|
||||
current_theme={(*current_theme).clone()}
|
||||
|
||||
@@ -108,10 +108,11 @@ pub fn Calendar(props: &CalendarProps) -> Html {
|
||||
let external_events = props.external_calendar_events.clone(); // Clone before the effect
|
||||
let view = props.view.clone(); // Clone before the effect
|
||||
|
||||
use_effect_with((*current_date, view.clone(), external_events.len()), move |(date, _view, _external_len)| {
|
||||
use_effect_with((*current_date, view.clone(), external_events.len(), props.user_info.clone()), move |(date, _view, _external_len, user_info)| {
|
||||
let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
|
||||
let date = *date; // Clone the date to avoid lifetime issues
|
||||
let external_events = external_events.clone(); // Clone external events to avoid lifetime issues
|
||||
let user_info = user_info.clone(); // Clone user_info to avoid lifetime issues
|
||||
|
||||
if let Some(token) = auth_token {
|
||||
let events = events.clone();
|
||||
@@ -148,8 +149,24 @@ pub fn Calendar(props: &CalendarProps) -> Html {
|
||||
.await
|
||||
{
|
||||
Ok(vevents) => {
|
||||
// Combine regular events with external calendar events
|
||||
let mut all_events = vevents;
|
||||
// Filter CalDAV events based on calendar visibility
|
||||
let mut filtered_events = if let Some(user_info) = user_info.as_ref() {
|
||||
vevents.into_iter()
|
||||
.filter(|event| {
|
||||
if let Some(calendar_path) = event.calendar_path.as_ref() {
|
||||
// Find the calendar info for this event
|
||||
user_info.calendars.iter()
|
||||
.find(|cal| &cal.path == calendar_path)
|
||||
.map(|cal| cal.is_visible)
|
||||
.unwrap_or(true) // Default to visible if not found
|
||||
} else {
|
||||
true // Show events without calendar path
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vevents // Show all events if no user info
|
||||
};
|
||||
|
||||
// Mark external events as external by adding a special category
|
||||
let marked_external_events: Vec<VEvent> = external_events
|
||||
@@ -161,9 +178,9 @@ pub fn Calendar(props: &CalendarProps) -> Html {
|
||||
})
|
||||
.collect();
|
||||
|
||||
all_events.extend(marked_external_events);
|
||||
filtered_events.extend(marked_external_events);
|
||||
|
||||
let grouped_events = CalendarService::group_events_by_date(all_events);
|
||||
let grouped_events = CalendarService::group_events_by_date(filtered_events);
|
||||
events.set(grouped_events);
|
||||
loading.set(false);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ pub struct CalendarListItemProps {
|
||||
pub on_color_picker_toggle: Callback<String>, // calendar_path
|
||||
pub available_colors: Vec<String>,
|
||||
pub on_context_menu: Callback<(MouseEvent, String)>, // (event, calendar_path)
|
||||
pub on_visibility_toggle: Callback<String>, // calendar_path
|
||||
}
|
||||
|
||||
#[function_component(CalendarListItem)]
|
||||
@@ -32,44 +33,59 @@ pub fn calendar_list_item(props: &CalendarListItemProps) -> Html {
|
||||
})
|
||||
};
|
||||
|
||||
let on_visibility_toggle = {
|
||||
let cal_path = props.calendar.path.clone();
|
||||
let on_visibility_toggle = props.on_visibility_toggle.clone();
|
||||
Callback::from(move |_| {
|
||||
on_visibility_toggle.emit(cal_path.clone());
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<li key={props.calendar.path.clone()} oncontextmenu={on_context_menu}>
|
||||
<span class="calendar-color"
|
||||
style={format!("background-color: {}", props.calendar.color)}
|
||||
onclick={on_color_click}>
|
||||
{
|
||||
if props.color_picker_open {
|
||||
html! {
|
||||
<div class="color-picker">
|
||||
{
|
||||
props.available_colors.iter().map(|color| {
|
||||
let color_str = color.clone();
|
||||
let cal_path = props.calendar.path.clone();
|
||||
let on_color_change = props.on_color_change.clone();
|
||||
<div class="calendar-info">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.calendar.is_visible}
|
||||
onchange={on_visibility_toggle}
|
||||
/>
|
||||
<span class="calendar-color"
|
||||
style={format!("background-color: {}", props.calendar.color)}
|
||||
onclick={on_color_click}>
|
||||
{
|
||||
if props.color_picker_open {
|
||||
html! {
|
||||
<div class="color-picker">
|
||||
{
|
||||
props.available_colors.iter().map(|color| {
|
||||
let color_str = color.clone();
|
||||
let cal_path = props.calendar.path.clone();
|
||||
let on_color_change = props.on_color_change.clone();
|
||||
|
||||
let on_color_select = Callback::from(move |_: MouseEvent| {
|
||||
on_color_change.emit((cal_path.clone(), color_str.clone()));
|
||||
});
|
||||
let on_color_select = Callback::from(move |_: MouseEvent| {
|
||||
on_color_change.emit((cal_path.clone(), color_str.clone()));
|
||||
});
|
||||
|
||||
let is_selected = props.calendar.color == *color;
|
||||
let class_name = if is_selected { "color-option selected" } else { "color-option" };
|
||||
let is_selected = props.calendar.color == *color;
|
||||
let class_name = if is_selected { "color-option selected" } else { "color-option" };
|
||||
|
||||
html! {
|
||||
<div class={class_name}
|
||||
style={format!("background-color: {}", color)}
|
||||
onclick={on_color_select}>
|
||||
</div>
|
||||
}
|
||||
}).collect::<Html>()
|
||||
}
|
||||
</div>
|
||||
html! {
|
||||
<div class={class_name}
|
||||
style={format!("background-color: {}", color)}
|
||||
onclick={on_color_select}>
|
||||
</div>
|
||||
}
|
||||
}).collect::<Html>()
|
||||
}
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
}
|
||||
</span>
|
||||
<span class="calendar-name">{&props.calendar.display_name}</span>
|
||||
</span>
|
||||
<span class="calendar-name">{&props.calendar.display_name}</span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ pub struct SidebarProps {
|
||||
pub on_color_picker_toggle: Callback<String>,
|
||||
pub available_colors: Vec<String>,
|
||||
pub on_calendar_context_menu: Callback<(MouseEvent, String)>,
|
||||
pub on_calendar_visibility_toggle: Callback<String>,
|
||||
pub current_view: ViewMode,
|
||||
pub on_view_change: Callback<ViewMode>,
|
||||
pub current_theme: Theme,
|
||||
@@ -221,6 +222,7 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
||||
on_color_picker_toggle={props.on_color_picker_toggle.clone()}
|
||||
available_colors={props.available_colors.clone()}
|
||||
on_context_menu={props.on_calendar_context_menu.clone()}
|
||||
on_visibility_toggle={props.on_calendar_visibility_toggle.clone()}
|
||||
/>
|
||||
}
|
||||
}).collect::<Html>()
|
||||
|
||||
@@ -44,6 +44,7 @@ pub struct CalendarInfo {
|
||||
pub path: String,
|
||||
pub display_name: String,
|
||||
pub color: String,
|
||||
pub is_visible: bool,
|
||||
}
|
||||
|
||||
// CalendarEvent, EventStatus, and EventClass are now imported from shared library
|
||||
|
||||
@@ -3822,6 +3822,29 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* CalDAV Calendar Styles */
|
||||
.calendar-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calendar-info:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.calendar-info input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
accent-color: rgba(255, 255, 255, 0.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Create External Calendar Button */
|
||||
.create-external-calendar-button {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
|
||||
Reference in New Issue
Block a user