feat: P2.2 Create Appointment from Email + P2.4 Calendar Dashlet + P2.12 Action Wheel + P2.14 Share Files

- P2.2: 'Create Appointment' button in email viewer → pre-fills event modal
  with subject, body, participants, date. calendar-store newEventPrefill state.
- P2.4: MiniCalendarDashlet in sidebar bottom — month grid with event dots,
  day click navigates to calendar. Collapsible, respect firstDayOfWeek.
- P2.12: Custom radial menu (components/ui/radial-menu.tsx) — circular SVG
  menu with keyboard nav, animations. Wired into email-list, contact-list,
  file-browser, calendar-month-view right-click handlers.
- P2.14: 'Send as Attachment' button in file browser — opens compose tab
  with selected files pre-attached via Pro tab store.
This commit is contained in:
Bernd Rodler
2026-08-07 13:15:04 +02:00
parent 67f61f18d0
commit 83e29b3ef1
14 changed files with 933 additions and 42 deletions
+6
View File
@@ -251,6 +251,9 @@ interface CalendarStore {
refreshICalSubscription: (client: IJMAPClient, subscriptionId: string) => Promise<void>;
refreshAllSubscriptions: (client: IJMAPClient) => Promise<void>;
isSubscriptionCalendar: (calendarId: string) => boolean;
newEventPrefill: { title?: string; description?: string; participants?: { name?: string; email: string }[]; date?: string } | null;
setNewEventPrefill: (prefill: { title?: string; description?: string; participants?: { name?: string; email: string }[]; date?: string } | null) => void;
}
const initialState = {
@@ -265,6 +268,7 @@ const initialState = {
error: null as string | null,
dateRange: null as { start: string; end: string } | null,
icalSubscriptions: [] as ICalSubscription[],
newEventPrefill: null as { title?: string; description?: string; participants?: { name?: string; email: string }[]; date?: string } | null,
};
function getSafeCalendarViewMode(value: unknown): CalendarViewMode {
@@ -1009,6 +1013,8 @@ export const useCalendarStore = create<CalendarStore>()(
return get().icalSubscriptions.some(s => s.calendarId === calendarId);
},
setNewEventPrefill: (prefill) => set({ newEventPrefill: prefill }),
addICalSubscription: async (client, url, name, color, refreshInterval = 60) => {
// Normalize webcal(s):// → https:// so the server-side fetcher
// (which only accepts http/https) doesn't reject every refresh.