mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
fix: add an invitation only to a calendar that shows events in the account on screen
This commit is contained in:
@@ -465,6 +465,33 @@ describe('CalendarInvitationBanner', () => {
|
||||
expect(screen.getByRole('heading', { name: 'Planning Session' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// pim-p2-2: "Add to calendar" offered the account's task list, where the
|
||||
// event would be stored but never shown, and other logins' calendars.
|
||||
it('adds to the one calendar that shows events, never a task list or another login', async () => {
|
||||
const before = mocks.calendarState.calendars;
|
||||
mocks.calendarState.calendars = [
|
||||
{ id: 'cal-1', name: 'Primary', color: '#2563eb', isDefault: true },
|
||||
{ id: 'todo', name: 'To-do', color: '#16a34a', isDefault: false, isTasksOnly: true },
|
||||
{ id: 'other', name: 'Other login', color: '#dc2626', isDefault: true, localAccountId: 'login-b' },
|
||||
] as never;
|
||||
try {
|
||||
mocks.calendarState.importEvents.mockResolvedValue(1);
|
||||
mocks.clientMock.parseCalendarEvents.mockResolvedValue([
|
||||
{ uid: 'uid-tl', title: 'Target check', start: '2026-03-22T13:00:00Z', participants: {
|
||||
attendee: { '@type': 'Participant', email: 'user@example.com', roles: { attendee: true }, participationStatus: 'needs-action' },
|
||||
} },
|
||||
]);
|
||||
render(<CalendarInvitationBanner email={makeEmail({ id: 'email-tl' })} />);
|
||||
await screen.findByRole('heading', { name: 'Target check' });
|
||||
fireEvent.click(screen.getByText('Add to calendar'));
|
||||
await waitFor(() => expect(mocks.calendarState.importEvents).toHaveBeenCalledTimes(1));
|
||||
expect(JSON.stringify(mocks.calendarState.importEvents.mock.calls[0])).toContain('cal-1');
|
||||
expect(screen.queryByText('To-do')).toBeNull();
|
||||
} finally {
|
||||
mocks.calendarState.calendars = before;
|
||||
}
|
||||
});
|
||||
|
||||
it('does not show RSVP actions when the current user is the organizer', async () => {
|
||||
mocks.clientMock.parseCalendarEvents.mockResolvedValue([
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import { answerInvitation } from '@/lib/calendar-rsvp';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
@@ -27,6 +27,7 @@ import { isDocumentRTL } from '@/i18n/direction';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { eventTargets } from '@/lib/calendar-targets';
|
||||
import { useCalendarStore } from '@/stores/calendar-store';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import type { Email, CalendarEvent } from '@/lib/jmap/types';
|
||||
@@ -455,7 +456,7 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
||||
const timeFormat = useSettingsStore((s) => s.timeFormat);
|
||||
// The user's zone, as getEffectiveTimeZone resolves it.
|
||||
const userTimeZone = resolveTimeZone(useSettingsStore((s) => s.timeZone));
|
||||
const { calendars, supportsCalendar, importEvents, rsvpEvent, updateEvent, deleteEvent, events: storeEvents, setSelectedDate } = useCalendarStore(useShallow((state) => ({
|
||||
const { calendars: allCalendars, supportsCalendar, importEvents, rsvpEvent, updateEvent, deleteEvent, events: storeEvents, setSelectedDate } = useCalendarStore(useShallow((state) => ({
|
||||
calendars: state.calendars,
|
||||
supportsCalendar: state.supportsCalendar,
|
||||
importEvents: state.importEvents,
|
||||
@@ -466,6 +467,16 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
||||
setSelectedDate: state.setSelectedDate,
|
||||
})));
|
||||
|
||||
// Where an invitation can go: the calendars of the account on screen that show
|
||||
// in the grid. A task list would hide the event, and another login's calendar
|
||||
// would put the meeting in the wrong account (pim-p2-2).
|
||||
const activeLocalAccountId = useAuthStore((s) => s.activeAccountId);
|
||||
const calendars = useMemo(() => {
|
||||
const mine = allCalendars.filter((c) => !c.localAccountId || c.localAccountId === activeLocalAccountId);
|
||||
const targets = eventTargets(mine);
|
||||
return targets.length > 0 ? targets : mine;
|
||||
}, [allCalendars, activeLocalAccountId]);
|
||||
|
||||
const [state, setState] = useState<BannerState>('loading');
|
||||
const [parsedEvent, setParsedEvent] = useState<Partial<CalendarEvent> | null>(null);
|
||||
const [rsvpStatus, setRsvpStatus] = useState<string | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user