From 813dcfe7f95dd0e2657c64ebb1e7b7a5d6cee164 Mon Sep 17 00:00:00 2001 From: ayusht2810 Date: Wed, 17 Jul 2024 19:27:23 +0530 Subject: [PATCH 1/2] [MM-595] Add feature to view shared calendars while creating event --- gcal/calendar.go | 20 +++++- gcal/event.go | 9 ++- go.mod | 2 +- go.sum | 4 +- webapp/src/actions.ts | 21 ++++++ webapp/src/components/calendar_selector.tsx | 67 +++++++++++++++++++ .../components/modals/create_event_form.tsx | 10 +++ webapp/src/types/calendar_api_types.ts | 1 + 8 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 webapp/src/components/calendar_selector.tsx diff --git a/gcal/calendar.go b/gcal/calendar.go index eae1c86..e2134a3 100644 --- a/gcal/calendar.go +++ b/gcal/calendar.go @@ -25,7 +25,25 @@ func (c *client) DeleteCalendar(remoteUserID string, calID string) error { // GetCalendars returns a list of calendars func (c *client) GetCalendars(remoteUserID string) ([]*remote.Calendar, error) { - return nil, errors.New("gcal GetCalendars not implemented") + service, err := calendar.NewService(context.Background(), option.WithHTTPClient(c.httpClient)) + if err != nil { + return nil, errors.Wrap(err, "gcal GetNotificationData, error creating service") + } + + res, err := service.CalendarList.List().Do() + if err != nil { + return nil, errors.Wrap(err, "gcal GetNotificationData, error getting list of calendars") + } + + calendarList := []*remote.Calendar{} + for _, calendar := range res.Items { + calendarList = append(calendarList, &remote.Calendar{ + ID: calendar.Id, + Name: calendar.Summary, + }) + } + + return calendarList, nil } // GetDefaultCalendar returns the default calendar for the user diff --git a/gcal/event.go b/gcal/event.go index 98f0577..0f9d09c 100644 --- a/gcal/event.go +++ b/gcal/event.go @@ -19,16 +19,19 @@ func (c *client) GetEvent(remoteUserID, eventID string) (*remote.Event, error) { } // CreateEvent creates a calendar event -func (c *client) CreateEvent(_ string, in *remote.Event) (*remote.Event, error) { +func (c *client) CreateEvent(calendarID, _ string, in *remote.Event) (*remote.Event, error) { service, err := calendar.NewService(context.Background(), option.WithHTTPClient(c.httpClient)) if err != nil { return nil, errors.Wrap(err, "gcal CreateEvent, error creating service") } evt := convertRemoteEventToGcalEvent(in) - + calendar := defaultCalendarName + if calendarID != "" { + calendar = calendarID + } resultEvent, err := service.Events. - Insert(defaultCalendarName, evt). + Insert(calendar, evt). SendUpdates("all"). // Send notifications to all attendees. Do() if err != nil { diff --git a/go.mod b/go.mod index 01c0b08..45871cf 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/mattermost/mattermost-plugin-google-calendar go 1.21 require ( - github.com/mattermost/mattermost-plugin-mscalendar v1.3.0 + github.com/mattermost/mattermost-plugin-mscalendar v1.2.2-0.20240717132440-5de872875282 github.com/mattermost/mattermost/server/public v0.1.1 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index cf0f089..d1ba7f3 100644 --- a/go.sum +++ b/go.sum @@ -146,8 +146,8 @@ github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI= github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy52be4= github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc= -github.com/mattermost/mattermost-plugin-mscalendar v1.3.0 h1:5X/J7O956A0uqENvDTrNVgLqWNuBKKXDE5jqlrbOCCY= -github.com/mattermost/mattermost-plugin-mscalendar v1.3.0/go.mod h1:aIO3y2VNX7VRmI2wvos8MFywGoqFp3zKH6QgyAJMEMQ= +github.com/mattermost/mattermost-plugin-mscalendar v1.2.2-0.20240717132440-5de872875282 h1:8vFteChSYt654Ae+EUrRz/l+bIroAB3pyq4q8V34CsQ= +github.com/mattermost/mattermost-plugin-mscalendar v1.2.2-0.20240717132440-5de872875282/go.mod h1:0QMN1lB9ChMWlJzUvC4JPvuAQ882hhwIWwT2kW7GAvo= github.com/mattermost/mattermost/server/public v0.1.1 h1:T5UtZ0SB3rZvhiKFUxkPn4fNrEQTXAcmCHVkRct1dpk= github.com/mattermost/mattermost/server/public v0.1.1/go.mod h1:WeqCPudYLqk4HjjGvCMJwhtHMVvcNUTHIbrLmLjAD+4= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= diff --git a/webapp/src/actions.ts b/webapp/src/actions.ts index 829958d..1bfd4ff 100644 --- a/webapp/src/actions.ts +++ b/webapp/src/actions.ts @@ -89,6 +89,27 @@ export const autocompleteUserChannels = (input: string, teamId: string) => async } }; +type Calendar = { + id: string; + name: string; +} + +export type CalendarListResponse = {data?: Calendar[]; error?: string}; + +export const fetchCalendarList = () => async (dispatch, getState): Promise => { + const state = getState(); + const pluginServerRoute = getPluginServerRoute(state); + + return doFetchWithResponse(`${pluginServerRoute}/api/v1/calendar/list`). + then((response) => { + return {data: response.data}; + }). + catch((response) => { + const error = response.message?.error || 'An error occurred while fetching calendars list.'; + return {data: [], error}; + }); +}; + export type CreateCalendarEventResponse = {data?: any; error?: string}; export const createCalendarEvent = (payload: CreateEventPayload) => async (dispatch, getState): Promise => { diff --git a/webapp/src/components/calendar_selector.tsx b/webapp/src/components/calendar_selector.tsx new file mode 100644 index 0000000..6351da2 --- /dev/null +++ b/webapp/src/components/calendar_selector.tsx @@ -0,0 +1,67 @@ +import React, {useCallback, useState} from 'react'; +import {useSelector, useDispatch} from 'react-redux'; + +import AsyncSelect from 'react-select/async'; + +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + +import {getStyleForReactSelect} from '@/utils/styles'; +import {CalendarListResponse, fetchCalendarList} from '@/actions'; + +type SelectOption = { + label: string; + value: string; +} + +type Props = { + onChange: (selected: string) => void; + value: string[]; +}; + +export default function CalendarSelector(props: Props) { + const [storedError, setStoredError] = useState(''); + + const theme = useSelector(getTheme); + + const dispatch = useDispatch(); + + const loadOptions = useCallback(async (): Promise => { + const response = (await dispatch(fetchCalendarList()) as unknown as CalendarListResponse); + + if (response.error) { + setStoredError(response.error); + return []; + } + + setStoredError(''); + + return response.data.map((c) => ({ + label: c.name, + value: c.id, + })); + }, []); + + const handleChange = (selected: SelectOption) => { + props.onChange(selected.value); + }; + + return ( + <> + + {storedError && ( +
+ {storedError} +
+ )} + + ); +} diff --git a/webapp/src/components/modals/create_event_form.tsx b/webapp/src/components/modals/create_event_form.tsx index 3a8df1e..23e9a8a 100644 --- a/webapp/src/components/modals/create_event_form.tsx +++ b/webapp/src/components/modals/create_event_form.tsx @@ -24,6 +24,7 @@ import {CreateCalendarEventResponse, createCalendarEvent} from '@/actions'; import {getTodayString} from '@/utils/datetime'; import './create_event_form.scss'; +import CalendarSelector from '../calendar_selector'; type Props = { close: (e?: Event) => void; @@ -46,6 +47,7 @@ export default function CreateEventForm(props: Props) { description: '', channel_id: '', location: '', + calendar_id: '', }); const setFormValue = (name: Key, value: CreateEventPayload[Key]) => { @@ -167,6 +169,14 @@ const ActualForm = (props: ActualFormProps) => { const theme = useSelector(getTheme); const components = [ + { + label: 'Calendar (optional)', + component: ( + setFormValue('calendar_id', selected)} + /> + ), + }, { label: 'Subject', required: true, diff --git a/webapp/src/types/calendar_api_types.ts b/webapp/src/types/calendar_api_types.ts index 787cb61..490748f 100644 --- a/webapp/src/types/calendar_api_types.ts +++ b/webapp/src/types/calendar_api_types.ts @@ -9,4 +9,5 @@ export type CreateEventPayload = { subject: string; location?: string; channel_id?: string; + calendar_id?: string; } From f848460132d1803e897b021b4b9539554936d9bc Mon Sep 17 00:00:00 2001 From: ayusht2810 Date: Mon, 22 Jul 2024 14:50:12 +0530 Subject: [PATCH 2/2] [MM-595] Update error statement and component to remove extra div --- gcal/calendar.go | 4 ++-- webapp/src/components/calendar_selector.tsx | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/gcal/calendar.go b/gcal/calendar.go index e2134a3..b2572b3 100644 --- a/gcal/calendar.go +++ b/gcal/calendar.go @@ -27,12 +27,12 @@ func (c *client) DeleteCalendar(remoteUserID string, calID string) error { func (c *client) GetCalendars(remoteUserID string) ([]*remote.Calendar, error) { service, err := calendar.NewService(context.Background(), option.WithHTTPClient(c.httpClient)) if err != nil { - return nil, errors.Wrap(err, "gcal GetNotificationData, error creating service") + return nil, errors.Wrap(err, "gcal GetCalendars, error creating service") } res, err := service.CalendarList.List().Do() if err != nil { - return nil, errors.Wrap(err, "gcal GetNotificationData, error getting list of calendars") + return nil, errors.Wrap(err, "gcal GetCalendars, error getting list of calendars") } calendarList := []*remote.Calendar{} diff --git a/webapp/src/components/calendar_selector.tsx b/webapp/src/components/calendar_selector.tsx index 6351da2..7b475fe 100644 --- a/webapp/src/components/calendar_selector.tsx +++ b/webapp/src/components/calendar_selector.tsx @@ -58,9 +58,7 @@ export default function CalendarSelector(props: Props) { isMulti={false} /> {storedError && ( -
- {storedError} -
+ {storedError} )} );