Merge pull request #1564 from Abderahman88/RecurringEvents
This commit is contained in:
commit
3d0e617e5b
|
@ -39,6 +39,7 @@ Version|Date|Comments
|
|||
-------|----|--------
|
||||
1.0 |December 29, 2019 | Initial Release
|
||||
1.1 |January 08, 2020 | Bumped to SPFx 1.10 and added the Personal Tab support
|
||||
1.2 |October 27, 2020 | Recurring events support
|
||||
|
||||
## Disclaimer
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"solution": {
|
||||
"name": "react-graph-calendar-client-side-solution",
|
||||
"id": "42fe0a0f-c4d9-4b05-806c-3857decb3d71",
|
||||
"version": "1.0.0.0",
|
||||
"version": "1.0.1.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
|
|
|
@ -7093,7 +7093,6 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
|
@ -7697,7 +7696,6 @@
|
|||
"version": "0.10.53",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es6-iterator": "~2.0.3",
|
||||
"es6-symbol": "~3.1.3",
|
||||
|
@ -7713,7 +7711,6 @@
|
|||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
|
@ -7777,7 +7774,6 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
|
@ -8171,7 +8167,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||
"integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type": "^2.0.0"
|
||||
},
|
||||
|
@ -8179,8 +8174,7 @@
|
|||
"type": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
|
||||
"integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13579,6 +13573,14 @@
|
|||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||
},
|
||||
"moment-range": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/moment-range/-/moment-range-4.0.2.tgz",
|
||||
"integrity": "sha512-n8sceWwSTjmz++nFHzeNEUsYtDqjgXgcOBzsHi+BoXQU2FW+eU92LUaK8gqOiSu5PG57Q9sYj1Fz4LRDj4FtKA==",
|
||||
"requires": {
|
||||
"es6-symbol": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.27",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
|
||||
|
@ -13711,8 +13713,7 @@
|
|||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||
},
|
||||
"nice-try": {
|
||||
"version": "1.0.5",
|
||||
|
@ -18013,8 +18014,7 @@
|
|||
"type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.3.2",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-range": "^4.0.2",
|
||||
"moment-timezone": "^0.5.27",
|
||||
"office-ui-fabric-react": "6.189.2",
|
||||
"react": "16.8.5",
|
||||
|
|
|
@ -3,7 +3,6 @@ import * as ReactDom from 'react-dom';
|
|||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
|
@ -12,10 +11,11 @@ import GraphCalendar from './components/GraphCalendar';
|
|||
import { IGraphCalendarProps } from './components/IGraphCalendarProps';
|
||||
import * as microsoftTeams from '@microsoft/teams-js';
|
||||
import { initializeIcons } from 'office-ui-fabric-react';
|
||||
import { PropertyPaneSlider } from '@microsoft/sp-property-pane';
|
||||
import { PropertyPaneSlider, PropertyPaneCheckbox, IPropertyPaneConfiguration } from '@microsoft/sp-property-pane';
|
||||
|
||||
export interface IGraphCalendarWebPartProps {
|
||||
limit: number;
|
||||
showRecurrence: boolean;
|
||||
}
|
||||
|
||||
export default class GraphCalendarWebPart extends BaseClientSideWebPart<IGraphCalendarWebPartProps> {
|
||||
|
@ -26,6 +26,7 @@ export default class GraphCalendarWebPart extends BaseClientSideWebPart<IGraphCa
|
|||
GraphCalendar,
|
||||
{
|
||||
limit: this.properties.limit,
|
||||
showRecurrence: this.properties.showRecurrence,
|
||||
context: this.context,
|
||||
teamsContext: this._teamsContext
|
||||
}
|
||||
|
@ -43,6 +44,10 @@ export default class GraphCalendarWebPart extends BaseClientSideWebPart<IGraphCa
|
|||
this.properties.limit = 100;
|
||||
}
|
||||
|
||||
if (this.properties.showRecurrence === undefined) {
|
||||
this.properties.showRecurrence = true;
|
||||
}
|
||||
|
||||
// Sets the Teams context if in Teams
|
||||
if (this.context.sdks.microsoftTeams) {
|
||||
this._teamsContext = this.context.sdks.microsoftTeams.context;
|
||||
|
@ -82,6 +87,10 @@ export default class GraphCalendarWebPart extends BaseClientSideWebPart<IGraphCa
|
|||
label: "Events to load per active view",
|
||||
max: 500,
|
||||
min: 50
|
||||
}),
|
||||
PropertyPaneCheckbox('showRecurrence', {
|
||||
text: "Show recurring events",
|
||||
checked: true
|
||||
})
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ import { EventInput } from '@fullcalendar/core';
|
|||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import * as moment from 'moment-timezone';
|
||||
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
|
||||
import { extendMoment } from 'moment-range';
|
||||
|
||||
const { range } = extendMoment(moment);
|
||||
|
||||
interface IGraphCalendarState {
|
||||
events: EventInput[];
|
||||
|
@ -36,10 +39,10 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
super(props);
|
||||
|
||||
// If this is running in Teams, embed the specific Teams styling
|
||||
if(this._isRunningInTeams()) {
|
||||
if (this._isRunningInTeams()) {
|
||||
import("./GraphCalendar.Teams.module.scss");
|
||||
|
||||
if(this.props.teamsContext.theme == "dark") {
|
||||
if (this.props.teamsContext.theme == "dark") {
|
||||
import("./GraphCalendar.Teams.Dark.module.scss");
|
||||
}
|
||||
}
|
||||
|
@ -73,11 +76,11 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
*/
|
||||
public render(): React.ReactElement<IGraphCalendarProps> {
|
||||
return (
|
||||
<div className={ styles.graphCalendar }>
|
||||
<FullCalendar
|
||||
<div className={styles.graphCalendar}>
|
||||
<FullCalendar
|
||||
ref={this.calendar}
|
||||
defaultView="dayGridMonth"
|
||||
plugins={[ dayGridPlugin ]}
|
||||
defaultView="dayGridMonth"
|
||||
plugins={[dayGridPlugin]}
|
||||
windowResize={this._handleResize.bind(this)}
|
||||
datesRender={this._datesRender.bind(this)}
|
||||
eventClick={this._openEventPanel.bind(this)}
|
||||
|
@ -86,7 +89,7 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
{this.state.currentSelectedEvent &&
|
||||
<Panel
|
||||
isOpen={this.state.isEventDetailsOpen}
|
||||
type={ PanelType.smallFixedFar }
|
||||
type={PanelType.smallFixedFar}
|
||||
headerText={this.state.currentSelectedEvent ? this.state.currentSelectedEvent.title : ""}
|
||||
onDismiss={this._closeEventPanel.bind(this)}
|
||||
isLightDismiss={true}
|
||||
|
@ -95,14 +98,14 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
<span>{moment(this.state.currentSelectedEvent.start).format('MMMM Do YYYY [at] h:mm:ss a')}</span>
|
||||
<h3>Start Time</h3>
|
||||
<span>{moment(this.state.currentSelectedEvent.end).format('MMMM Do YYYY [at] h:mm:ss a')}</span>
|
||||
{this.state.currentSelectedEvent.extendedProps["location"] &&
|
||||
{this.state.currentSelectedEvent.extendedProps["location"] &&
|
||||
<div>
|
||||
<h3>Location</h3>
|
||||
<span>{this.state.currentSelectedEvent.extendedProps["location"]}</span>
|
||||
</div>
|
||||
}
|
||||
{this.state.currentSelectedEvent.extendedProps["body"] &&
|
||||
<div>
|
||||
{this.state.currentSelectedEvent.extendedProps["body"] &&
|
||||
<div>
|
||||
<h3>Body</h3>
|
||||
<span>{this.state.currentSelectedEvent.extendedProps["body"]}</span>
|
||||
</div>
|
||||
|
@ -118,7 +121,7 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
* Mainly used for Teams validation so it renders "full-screen" in Teams
|
||||
*/
|
||||
private _calculateHeight(): number {
|
||||
if(this._isRunningInTeams()) {
|
||||
if (this._isRunningInTeams()) {
|
||||
return window.innerHeight - 30;
|
||||
} else {
|
||||
return 600;
|
||||
|
@ -138,7 +141,7 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
private _isPersonalTab() {
|
||||
let _isPersonalTab: Boolean = false;
|
||||
|
||||
if(this._isRunningInTeams() && !this.props.teamsContext.teamId) {
|
||||
if (this._isRunningInTeams() && !this.props.teamsContext.teamId) {
|
||||
_isPersonalTab = true;
|
||||
}
|
||||
|
||||
|
@ -165,16 +168,16 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
currentSelectedEvent: null
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the view changed, reload the events based on the active view
|
||||
* @param info Information about the current active view
|
||||
*/
|
||||
private _datesRender(info: any) {
|
||||
if(this.calendar.value) {
|
||||
if (this.calendar.value) {
|
||||
|
||||
// If the active view has changed
|
||||
if((this.state.currentActiveStartDate && this.state.currentActiveEndDate) && this.state.currentActiveStartDate.toString() != info.view.activeStart.toString() && this.state.currentActiveEndDate.toString() != info.view.activeEnd.toString()) {
|
||||
if ((this.state.currentActiveStartDate && this.state.currentActiveEndDate) && this.state.currentActiveStartDate.toString() != info.view.activeStart.toString() && this.state.currentActiveEndDate.toString() != info.view.activeEnd.toString()) {
|
||||
this._loadEvents(info.view.activeStart, info.view.activeEnd);
|
||||
}
|
||||
}
|
||||
|
@ -184,13 +187,89 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
* Handles the resize event when in Microsoft Teams to ensure a proper responsive behaviour
|
||||
*/
|
||||
private _handleResize() {
|
||||
if(this._isRunningInTeams()) {
|
||||
if (this._isRunningInTeams()) {
|
||||
this.setState({
|
||||
height: window.innerHeight - 30
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert data to Array<EventInput>
|
||||
* @param data Events from API
|
||||
*/
|
||||
private _transformEvents(data: any): Array<EventInput> {
|
||||
let events: Array<EventInput> = new Array<EventInput>();
|
||||
data.value.map((item: any) => {
|
||||
// Build a Timezone enabled Date
|
||||
let currentStartDate = moment.tz(item.start.dateTime, item.start.timeZone);
|
||||
let currentEndDate = moment.tz(item.end.dateTime, item.end.timeZone);
|
||||
|
||||
// Adding all retrieved events to the result array
|
||||
events.push({
|
||||
id: item.id,
|
||||
title: item.subject,
|
||||
|
||||
// If the event is an All Day event, add 1 day without Timezone to the start date
|
||||
start: !item.isAllDay ? currentStartDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentStartDate).add(1, 'd').toISOString(),
|
||||
|
||||
// If the event is an All Day event, add 1 day without Timezone to the end date
|
||||
end: !item.isAllDay ? currentEndDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentEndDate).add(1, 'd').toISOString(),
|
||||
allDay: item.isAllDay,
|
||||
location: item.location.displayName,
|
||||
body: item.bodyPreview,
|
||||
type: item.type
|
||||
});
|
||||
});
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the recurring events need to be shown on the current state of the Calendar
|
||||
* If the range of the recurring event overlaps the range of the Calendar, then the event needs to be shown.
|
||||
* @param data All the recurrent (base) events ever made
|
||||
* @param startDate The first visible date on the calendar
|
||||
* @param endDate The last visible date on the calendar
|
||||
*/
|
||||
private _filterRecEvents(data: any, startDate: Date, endDate: Date): Array<EventInput> {
|
||||
let events: Array<EventInput> = new Array<EventInput>();
|
||||
//Range of the Calendar
|
||||
var r1 = range(startDate, endDate);
|
||||
|
||||
data.value.map((item: any) => {
|
||||
// Build a Timezone enabled Date
|
||||
let currentStartDate = moment.tz(item.start.dateTime, item.start.timeZone);
|
||||
let currentEndDate = moment.tz(item.end.dateTime, item.end.timeZone);
|
||||
|
||||
var d1 = item.recurrence.range.startDate;
|
||||
var d2 = item.recurrence.range.endDate;
|
||||
var recStartDate = moment(d1).toDate();
|
||||
var recEndDate = moment(d2).toDate();
|
||||
|
||||
//Range of the recurring event item
|
||||
var r2 = range(recStartDate, recEndDate);
|
||||
|
||||
|
||||
//Check if both ranges overlap
|
||||
if (!!r1.overlaps(r2)) {
|
||||
events.push({
|
||||
id: item.id,
|
||||
title: item.subject,
|
||||
// If the event is an All Day event, add 1 day without Timezone to the start date
|
||||
start: !item.isAllDay ? currentStartDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentStartDate).add(1, 'd').toISOString(),
|
||||
// If the event is an All Day event, add 1 day without Timezone to the end date
|
||||
end: !item.isAllDay ? currentEndDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentEndDate).add(1, 'd').toISOString(),
|
||||
allDay: item.isAllDay,
|
||||
location: item.location.displayName,
|
||||
body: item.bodyPreview,
|
||||
type: item.type
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Events based on the current state of the Calendar
|
||||
* @param startDate The first visible date on the calendar
|
||||
|
@ -199,61 +278,129 @@ export default class GraphCalendar extends React.Component<IGraphCalendarProps,
|
|||
private _loadEvents(startDate: Date, endDate: Date): void {
|
||||
|
||||
// If a Group was found or running in the context of a Personal tab, execute the query. If not, do nothing.
|
||||
if(this.state.groupId || this.state.tabType == TabType.PersonalTab) {
|
||||
if (this.state.groupId || this.state.tabType == TabType.PersonalTab) {
|
||||
var events: Array<EventInput> = new Array<EventInput>();
|
||||
|
||||
this.props.context.msGraphClientFactory
|
||||
.getClient()
|
||||
.then((client: MSGraphClient): void => {
|
||||
.getClient()
|
||||
.then((client: MSGraphClient): void => {
|
||||
let apiUrl: string = `/groups/${this.state.groupId}/events`;
|
||||
if (this._isPersonalTab()) {
|
||||
apiUrl = '/me/events';
|
||||
}
|
||||
|
||||
let apiUrl: string = `/groups/${this.state.groupId}/events`;
|
||||
if(this._isPersonalTab()) {
|
||||
apiUrl = '/me/events';
|
||||
}
|
||||
client
|
||||
.api(apiUrl)
|
||||
.version("v1.0")
|
||||
.select('subject,start,end,location,bodyPreview,isAllDay,type')
|
||||
.filter(`start/dateTime ge '${startDate.toISOString()}' and end/dateTime le '${endDate.toISOString()}' and type eq 'singleInstance'`)
|
||||
.top(this.props.limit)
|
||||
.get((err, res) => {
|
||||
|
||||
client
|
||||
.api(apiUrl)
|
||||
.version("v1.0")
|
||||
.select('subject,start,end,location,bodyPreview,isAllDay')
|
||||
.filter(`start/dateTime ge '${startDate.toISOString()}' and end/dateTime le '${endDate.toISOString()}'`)
|
||||
.top(this.props.limit)
|
||||
.get((err, res) => {
|
||||
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var events: Array<EventInput> = new Array<EventInput>();
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
res.value.map((item: any) => {
|
||||
// Build a Timezone enabled Date
|
||||
let currentStartDate = moment.tz(item.start.dateTime, item.start.timeZone);
|
||||
let currentEndDate = moment.tz(item.end.dateTime, item.end.timeZone);
|
||||
//Transform API data to Array<EventInput>
|
||||
events = this._transformEvents(res);
|
||||
|
||||
// Adding all retrieved events to the result array
|
||||
events.push({
|
||||
title: item.subject,
|
||||
if (this.props.showRecurrence) {
|
||||
//Get recurring events and merge with the other (standard) events
|
||||
this._getRecurringMaster(startDate, endDate).then((recData: Array<EventInput>) => {
|
||||
this._getRecurrentEvents(recData, startDate, endDate).then((recEvents: Array<EventInput>) => {
|
||||
this.setState({
|
||||
events: [...recEvents, ...events].slice(0, this.props.limit),
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//Show only (standard) events
|
||||
this.setState({
|
||||
events: events,
|
||||
});
|
||||
}
|
||||
|
||||
// If the event is an All Day event, add 1 day without Timezone to the start date
|
||||
start: !item.isAllDay ? currentStartDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentStartDate).add(1, 'd').toISOString(),
|
||||
|
||||
// If the event is an All Day event, add 1 day without Timezone to the end date
|
||||
end: !item.isAllDay ? currentEndDate.clone().tz(Intl.DateTimeFormat().resolvedOptions().timeZone).format() : moment(currentEndDate).add(1, 'd').toISOString(),
|
||||
allDay: item.isAllDay,
|
||||
location: item.location.displayName,
|
||||
body: item.bodyPreview
|
||||
// Sets the state with current active calendar dates
|
||||
this.setState({
|
||||
currentActiveStartDate: startDate,
|
||||
currentActiveEndDate: endDate,
|
||||
currentSelectedEvent: null
|
||||
});
|
||||
});
|
||||
|
||||
// Sets the state with the retrieved events and current active calendar dates
|
||||
this.setState({
|
||||
events: events,
|
||||
currentActiveStartDate: startDate,
|
||||
currentActiveEndDate: endDate,
|
||||
currentSelectedEvent: null
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recurrent events based on the current state of the Calendar
|
||||
* @param events All the recurrent base events
|
||||
* @param startDate The first visible date on the calendar
|
||||
* @param endDate The last visible date on the calendar
|
||||
*/
|
||||
private _getRecurrentEvents(events: Array<EventInput>, startDate: Date, endDate: Date): Promise<Array<EventInput>> {
|
||||
return new Promise<Array<EventInput>>((resolve, reject) => {
|
||||
this.props.context.msGraphClientFactory
|
||||
.getClient()
|
||||
.then((client: MSGraphClient): void => {
|
||||
var recEvents: Array<EventInput> = new Array<EventInput>();
|
||||
var count = 0;
|
||||
events.map((item: any) => {
|
||||
let apiUrl: string = `/groups/${this.state.groupId}/events/${item.id}/instances?startDateTime=${startDate.toISOString()}&endDateTime=${endDate.toISOString()}`;
|
||||
if (this._isPersonalTab()) {
|
||||
apiUrl = `/me/events/${item.id}/instances?startDateTime=${startDate.toISOString()}&endDateTime=${endDate.toISOString()}`;
|
||||
}
|
||||
client
|
||||
.api(apiUrl)
|
||||
.version("v1.0")
|
||||
.select('subject,start,end,location,bodyPreview,isAllDay,type')
|
||||
.get((err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
recEvents = recEvents.concat(this._transformEvents(res));
|
||||
count += 1;
|
||||
if (count == events.length) {
|
||||
resolve(recEvents);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recurrent (base) events ever created
|
||||
* Filter the base events based on the current state of the Calendar
|
||||
* @param startDate The first visible date on the calendar
|
||||
* @param endDate The last visible date on the calendar
|
||||
*/
|
||||
private _getRecurringMaster(startDate: Date, endDate: Date): Promise<Array<EventInput>> {
|
||||
return new Promise<Array<EventInput>>((resolve, reject) => {
|
||||
this.props.context.msGraphClientFactory
|
||||
.getClient()
|
||||
.then((client: MSGraphClient): void => {
|
||||
let apiUrl: string = `/groups/${this.state.groupId}/events`;
|
||||
if (this._isPersonalTab()) {
|
||||
apiUrl = '/me/events';
|
||||
}
|
||||
client
|
||||
.api(apiUrl)
|
||||
.version("v1.0")
|
||||
.select('subject,start,end,location,bodyPreview,isAllDay,type,recurrence')
|
||||
.filter(`type eq 'seriesMaster'`) //recurrening event is type 'seriesMaster'
|
||||
.get((err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
var recEvents: Array<EventInput> = new Array<EventInput>();
|
||||
recEvents = this._filterRecEvents(res, startDate, endDate);
|
||||
resolve(recEvents);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as microsoftTeams from '@microsoft/teams-js';
|
|||
|
||||
export interface IGraphCalendarProps {
|
||||
limit: number;
|
||||
showRecurrence: boolean;
|
||||
context: WebPartContext;
|
||||
teamsContext: microsoftTeams.Context;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue