From 265f2a091bafd2328ab6480fecd67aa97f1c43de Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Fri, 10 Jan 2020 19:47:32 +0200 Subject: [PATCH 01/20] Event Edit: change container to Fragment --- src/components/EventEdit.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index 94cc579..a257af4 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -236,7 +236,7 @@ class EventEdit extends React.PureComponent { const differentTimezone = this.state.timezone && (this.state.timezone !== getCurrentTimezone()) && timezoneLoadFromName(this.state.timezone); return ( - + <>

{this.props.item ? 'Edit Event' : 'New Event'}

@@ -382,7 +382,7 @@ class EventEdit extends React.PureComponent { > Are you sure you would like to delete this event? -
+ ); } } From aae9cd5d1691b7d21ec51dc1eea370bd10d35141 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Fri, 10 Jan 2020 19:48:12 +0200 Subject: [PATCH 02/20] EventEdit: import RRuleEteSync --- src/components/EventEdit.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index a257af4..8748bf9 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -33,6 +33,7 @@ import * as EteSync from 'etesync'; import { getCurrentTimezone } from '../helpers'; import { EventType, timezoneLoadFromName } from '../pim-types'; +import RRuleEteSync, { RRuleOptions } from '../widgets/RRule'; interface PropsType { collections: EteSync.CollectionInfo[]; From 8d9482b720fc9acfbf50821c260a59a2ff3de27e Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sat, 11 Jan 2020 13:04:21 +0200 Subject: [PATCH 03/20] widgets: change RRule to get type ICAL.Recur as prop in ICAL.Recur many values are an array of numbers currently the RRule widget only handle multiple values for weekday. this change is not final since it may cause user data to be lost when editing an existing event --- src/widgets/RRule.tsx | 94 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 6c9034e..aa85190 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -2,33 +2,29 @@ import * as React from 'react'; import { TextField, Select, MenuItem, FormGroup, FormControlLabel, Checkbox, InputLabel, FormControl } from '@material-ui/core'; import DateTimePicker from '../widgets/DateTimePicker'; import { isNumber } from 'util'; +import * as ICAL from 'ical.js'; interface PropsType { onChange: (rrule: RRuleOptions) => void; rrule: RRuleOptions; } +type Frequency = 'YEARLY' | 'MONTHLY' | 'WEEKLY' | 'DAILY' | 'HOURLY' | 'MINUTELY' | 'SECONDLY'; + export interface RRuleOptions { freq: Frequency; - interval: number; - until?: Date; + interval?: number; + wkst?: WeekDay; + until?: ICAL.Time; count?: number; - byweekday?: Weekday[]; - bymonthday?: number; - byyearday?: number; - byweekno?: number; - bymonth?: Months; - bysetpos?: number; - wkst?: Weekday; bysecond?: number[]; byminute?: number[]; + byhour?: number[]; byday?: number[]; - -} -enum Frequency { - YEARLY, - MONTHLY, - WEEKLY, - DAILY, + bymonthday?: number[]; + byyearday?: number[]; + byweekno?: number[]; + bymonth?: number[]; + bysetpos?: number[]; } enum Ends { Never, @@ -53,14 +49,14 @@ enum MonthRepeat { Bysetpos, Bymonthday, } -enum Weekday { +enum WeekDay { + Su = 1, Mo, Tu, We, Th, Fr, Sa, - Su } const menuItemsMonths = Object.keys(Months).filter((key) => Number(key)).map((key) => { @@ -73,10 +69,10 @@ const menuItemsEnds = [Ends.Never, Ends.Date, Ends.After].map((key) => { {Ends[key]} ); }); -const weekdays = [Weekday.Mo, Weekday.Tu, Weekday.We, Weekday.Th, Weekday.Fr, Weekday.Sa, Weekday.Su]; -const menuItemsFrequency = [Frequency.YEARLY, Frequency.MONTHLY, Frequency.WEEKLY, Frequency.DAILY].map((value) => { +const weekdays = [WeekDay.Su, WeekDay.Mo, WeekDay.Tu, WeekDay.We, WeekDay.Th, WeekDay.Fr, WeekDay.Sa]; +const menuItemsFrequency = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'].map((value) => { return ( - {Frequency[value]} + {value} ); }); @@ -101,7 +97,7 @@ export default function RRuleEteSync(props: PropsType) { function handleCheckboxWeekday(event: React.FormEvent<{ value: unknown }>): void { const checkbox = event.target as HTMLInputElement; const weekday = Number(checkbox.value); - let byweekdayArray = options.byweekday as Weekday[]; + let byweekdayArray = options.byday as WeekDay[]; let byweekday; if (!checkbox.checked && byweekdayArray) { byweekday = byweekdayArray.filter((day) => day !== weekday); @@ -111,30 +107,29 @@ export default function RRuleEteSync(props: PropsType) { } else { byweekday = [weekday]; } - updateRule({ byweekday: byweekday }); + updateRule({ byday: byweekday }); } function isWeekdayChecked(day: number): boolean { - const weekdayArray = options.byweekday; - if (weekdayArray) { - return isNumber(weekdayArray.find((value) => Weekday[value] === Weekday[day])); + if (options.byday) { + return isNumber(options.byday.find((value) => WeekDay[value] === WeekDay[day])); } else { return false; } } - const checkboxWeekDays = weekdays.map((_, index) => { + const checkboxWeekDays = weekdays.map((day) => { return ( } - key={index} - label={Weekday[index]} /> + key={day} + label={WeekDay[day]} /> ); }); @@ -164,13 +159,13 @@ export default function RRuleEteSync(props: PropsType) { value={options.freq} style={{ alignSelf: 'flex-end', marginLeft: 20 }} onChange={(event: React.FormEvent<{ value: unknown }>) => { - const freq = Number((event.target as HTMLSelectElement).value); + const freq = (event.target as HTMLSelectElement).value as Frequency; const updatedOptions = { freq: freq, bysetpos: undefined, - bymonthday: freq === Frequency.MONTHLY || Frequency.YEARLY === freq ? 1 : undefined, + bymonthday: freq === 'MONTHLY' || 'YEARLY' === freq ? [1] : undefined, byweekday: undefined, - bymonth: freq === Frequency.YEARLY ? Months.Jan : undefined, + bymonth: freq === 'YEARLY' ? [Months.Jan] : undefined, }; updateRule(updatedOptions); }} @@ -180,15 +175,15 @@ export default function RRuleEteSync(props: PropsType) {
- {(options.freq === Frequency.MONTHLY) && + {(options.freq === 'MONTHLY') && ) => { - updateRule({ bysetpos: Number((event.target as HTMLInputElement).value) }); + updateRule({ bysetpos: [Number((event.target as HTMLInputElement).value)] }); }}> First Second @@ -211,11 +206,11 @@ export default function RRuleEteSync(props: PropsType) { Last } - {(options.freq === Frequency.YEARLY && options.bymonth) && + {(options.freq === 'YEARLY' && options.bymonth) && @@ -223,7 +218,7 @@ export default function RRuleEteSync(props: PropsType) { {options.bymonthday && 0) { - updateRule({ bymonthday: numberValue }); + updateRule({ bymonthday: [numberValue] }); } }} /> @@ -242,7 +237,7 @@ export default function RRuleEteSync(props: PropsType) { }
- {options.freq !== Frequency.DAILY && + {(options.freq && options.freq !== 'DAILY') && {checkboxWeekDays} } @@ -253,7 +248,7 @@ export default function RRuleEteSync(props: PropsType) { const value = Number((event.target as HTMLSelectElement).value); let updateOptions; if (value === Ends.Date) { - updateOptions = { count: undefined, until: new Date() }; + updateOptions = { count: undefined, until: ICAL.Time.now() }; } else if (value === Ends.After) { updateOptions = { until: undefined, count: 1 }; } else { @@ -267,9 +262,12 @@ export default function RRuleEteSync(props: PropsType) { {options.until && updateRule({ until: date })} + onChange={(date?: Date) => { + const value = date ? date : null; + updateRule({ until: ICAL.Time.fromJSDate(value, true) }); + }} /> } {options.count && From 7b17a14a841e851af14acd5b7a19a316efee1927 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sat, 11 Jan 2020 13:11:33 +0200 Subject: [PATCH 04/20] components: EventEdit add RRule widget --- src/components/EventEdit.tsx | 53 +++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index 8748bf9..a49d865 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -35,6 +35,7 @@ import { getCurrentTimezone } from '../helpers'; import { EventType, timezoneLoadFromName } from '../pim-types'; import RRuleEteSync, { RRuleOptions } from '../widgets/RRule'; + interface PropsType { collections: EteSync.CollectionInfo[]; initialCollection?: string; @@ -56,7 +57,7 @@ class EventEdit extends React.PureComponent { location: string; description: string; journalUid: string; - + rruleOptions?: RRuleOptions; error?: string; showDeleteDialog: boolean; }; @@ -70,7 +71,6 @@ class EventEdit extends React.PureComponent { location: '', description: '', timezone: null, - journalUid: '', showDeleteDialog: false, }; @@ -105,6 +105,7 @@ class EventEdit extends React.PureComponent { this.state.location = event.location ? event.location : ''; this.state.description = event.description ? event.description : ''; this.state.timezone = event.timezone; + this.state.rruleOptions = this.props.item?.component.getFirstPropertyValue('rrule'); } else { this.state.uid = uuid.v4(); } @@ -122,6 +123,8 @@ class EventEdit extends React.PureComponent { this.handleInputChange = this.handleInputChange.bind(this); this.toggleAllDay = this.toggleAllDay.bind(this); this.onDeleteRequest = this.onDeleteRequest.bind(this); + this.toggleRecurring = this.toggleRecurring.bind(this); + this.handleRRuleChange = this.handleRRuleChange.bind(this); } public UNSAFE_componentWillReceiveProps(nextProps: any) { @@ -155,7 +158,16 @@ class EventEdit extends React.PureComponent { public toggleAllDay() { this.setState({ allDay: !this.state.allDay }); } + public toggleRecurring() { + const value = this.state.rruleOptions ? undefined : { freq: 'WEEKLY', interval: 1 }; + this.setState({ rruleOptions: value }); + } + + public handleRRuleChange(rrule: RRuleOptions): void { + this.setState({ rruleOptions: rrule }); + console.log(rrule); + } public onSubmit(e: React.FormEvent) { e.preventDefault(); @@ -191,7 +203,7 @@ class EventEdit extends React.PureComponent { this.props.item.clone() : new EventType() - ; + ; event.uid = this.state.uid; event.summary = this.state.title; event.startDate = startDate; @@ -233,21 +245,17 @@ class EventEdit extends React.PureComponent { }, }; - const recurring = this.props.item && this.props.item.isRecurring(); + if (this.props.item && this.props.item.isRecurring()) { + console.log(1); + } const differentTimezone = this.state.timezone && (this.state.timezone !== getCurrentTimezone()) && timezoneLoadFromName(this.state.timezone); + return ( <>

{this.props.item ? 'Edit Event' : 'New Event'}

- {recurring && ( -
- IMPORTANT: - This is a recurring event, for now, only editing the whole series - (by editing the first instance) is supported. -
- )} {this.state.error && (
ERROR! {this.state.error}
)} @@ -336,7 +344,25 @@ class EventEdit extends React.PureComponent { value={this.state.description} onChange={this.handleInputChange} /> - + + + } + label="Recurring" + /> + + {this.state.rruleOptions && + + }
+ { onOk={() => this.props.onDelete(this.props.item!, this.props.initialCollection!)} onCancel={() => this.setState({ showDeleteDialog: false })} > - Are you sure you would like to delete this event? + Are you sure you would like to delete this event? ); From 86bd2a542038b67df808bd3708e10bfc99f9a1ab Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sat, 11 Jan 2020 14:54:19 +0200 Subject: [PATCH 05/20] widgets: RRule support bymonth repeat for multiple values --- src/widgets/RRule.tsx | 125 +++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 38 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index aa85190..de37845 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { TextField, Select, MenuItem, FormGroup, FormControlLabel, Checkbox, InputLabel, FormControl } from '@material-ui/core'; +import { TextField, Select, MenuItem, FormGroup, FormControlLabel, Checkbox, InputLabel, FormControl, FormLabel } from '@material-ui/core'; import DateTimePicker from '../widgets/DateTimePicker'; import { isNumber } from 'util'; import * as ICAL from 'ical.js'; @@ -58,21 +58,30 @@ enum WeekDay { Fr, Sa, } - -const menuItemsMonths = Object.keys(Months).filter((key) => Number(key)).map((key) => { - return ( - {Months[key]} - ); -}); const menuItemsEnds = [Ends.Never, Ends.Date, Ends.After].map((key) => { return ( {Ends[key]} ); }); const weekdays = [WeekDay.Su, WeekDay.Mo, WeekDay.Tu, WeekDay.We, WeekDay.Th, WeekDay.Fr, WeekDay.Sa]; +const months = [ + Months.Jan, + Months.Feb, + Months.Mar, + Months.Apr, + Months.May, + Months.Jun, + Months.Jul, + Months.Aug, + Months.Sep, + Months.Oct, + Months.Nov, + Months.Dec, +]; + const menuItemsFrequency = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'].map((value) => { return ( - {value} + {value.toLowerCase()} ); }); @@ -109,7 +118,28 @@ export default function RRuleEteSync(props: PropsType) { } updateRule({ byday: byweekday }); } - + function handleCheckboxMonth(event: React.FormEvent<{ value: unknown }>): void { + const checkbox = event.target as HTMLInputElement; + const month = Number(checkbox.value); + let bymonthArray = options.bymonth as Months[]; + let bymonth; + if (!checkbox.checked && bymonthArray) { + bymonth = bymonthArray.filter((day) => day !== month); + } else if (bymonthArray) { + bymonthArray = bymonthArray.filter((day) => day !== month); + bymonth = [...bymonthArray, month]; + } else { + bymonth = [month]; + } + updateRule({ bymonth: bymonth }); + } + function isMonthChecked(month: number): boolean { + if (options.bymonth) { + return isNumber(options.bymonth.find((value) => Months[value] === Months[month])); + } else { + return false; + } + } function isWeekdayChecked(day: number): boolean { if (options.byday) { return isNumber(options.byday.find((value) => WeekDay[value] === WeekDay[day])); @@ -117,6 +147,20 @@ export default function RRuleEteSync(props: PropsType) { return false; } } + const checkboxMonths = months.map((month) => { + return ( + } + key={month} + label={Months[month]} /> + ); + }); const checkboxWeekDays = weekdays.map((day) => { return ( @@ -173,8 +217,26 @@ export default function RRuleEteSync(props: PropsType) { {menuItemsFrequency}
+ {options.bymonthday && + ) => { + event.preventDefault(); + const value = (event.currentTarget as HTMLInputElement).value; + const numberValue = Number(value); + if (value === '') { + updateRule({ bymonthday: undefined }); + } else if (numberValue < 32 && numberValue > 0) { + updateRule({ bymonthday: [numberValue] }); + } + }} + /> + }
- {(options.freq === 'MONTHLY') && } - {(options.freq === 'YEARLY' && options.bymonth) && - - } - {options.bymonthday && - ) => { - event.preventDefault(); - const value = (event.currentTarget as HTMLInputElement).value; - const numberValue = Number(value); - if (value === '') { - updateRule({ bymonthday: undefined }); - } else if (numberValue < 32 && numberValue > 0) { - updateRule({ bymonthday: [numberValue] }); - } - }} - /> + + {(options.freq === 'YEARLY' && options.bymonth) && + + Months + + {checkboxMonths} + + }
+ {(options.freq && options.freq !== 'DAILY') && - {checkboxWeekDays} + + Weekdays + + {checkboxWeekDays} + + } Ends From 3f05e434397ef4d9820d42d6540becca2c9492c2 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sat, 11 Jan 2020 23:29:54 +0200 Subject: [PATCH 06/20] widgets: RRule disable complex recurring event options --- src/widgets/RRule.tsx | 108 ++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index de37845..891ab0d 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -9,7 +9,7 @@ interface PropsType { rrule: RRuleOptions; } type Frequency = 'YEARLY' | 'MONTHLY' | 'WEEKLY' | 'DAILY' | 'HOURLY' | 'MINUTELY' | 'SECONDLY'; - +const disableComplex = true; export interface RRuleOptions { freq: Frequency; interval?: number; @@ -236,58 +236,64 @@ export default function RRuleEteSync(props: PropsType) { }} /> } -
- {(options.freq === 'MONTHLY') && - - } - - - {options.bysetpos && - - } - - - {(options.freq === 'YEARLY' && options.bymonth) && - - Months - - {checkboxMonths} - - - } -
+ { + !disableComplex &&
+ {(options.freq === 'MONTHLY') && + } {options.bysetpos && + } +
}
- + {options.freq === 'YEARLY' && +
+ Months + +
+ } {(options.freq && options.freq !== 'DAILY') && - - Weekdays - - {checkboxWeekDays} - - +
+ Weekdays + +
} Ends From 2dcf86238b29514bc380f98353fa95e99ab29d9f Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sat, 11 Jan 2020 23:36:27 +0200 Subject: [PATCH 07/20] widgets: RRule change bymonth and byday to multi Select --- src/widgets/RRule.tsx | 165 +++++++++++++----------------------------- 1 file changed, 51 insertions(+), 114 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 891ab0d..46b5997 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { TextField, Select, MenuItem, FormGroup, FormControlLabel, Checkbox, InputLabel, FormControl, FormLabel } from '@material-ui/core'; +import { TextField, Select, MenuItem, FormControlLabel, InputLabel, FormControl } from '@material-ui/core'; import DateTimePicker from '../widgets/DateTimePicker'; -import { isNumber } from 'util'; import * as ICAL from 'ical.js'; interface PropsType { @@ -63,7 +62,15 @@ const menuItemsEnds = [Ends.Never, Ends.Date, Ends.After].map((key) => { {Ends[key]} ); }); -const weekdays = [WeekDay.Su, WeekDay.Mo, WeekDay.Tu, WeekDay.We, WeekDay.Th, WeekDay.Fr, WeekDay.Sa]; +const weekdays = [ + WeekDay.Su, + WeekDay.Mo, + WeekDay.Tu, + WeekDay.We, + WeekDay.Th, + WeekDay.Fr, + WeekDay.Sa, +]; const months = [ Months.Jan, Months.Feb, @@ -78,21 +85,27 @@ const months = [ Months.Nov, Months.Dec, ]; - const menuItemsFrequency = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'].map((value) => { return ( {value.toLowerCase()} ); }); - +const menuItemMonths = months.map((month) => { + return ( + {Months[month]} + ); +}); +const menuItemsWeekDays = weekdays.map((day) => { + return ( + {WeekDay[day]} + ); +}); export default function RRuleEteSync(props: PropsType) { const options = props.rrule; - function updateRule(newOptions: Partial): void { const updatedOptions = { ...options, ...newOptions }; props.onChange(updatedOptions); } - function getEnds(): Ends { if (options.until && !options.count) { return Ends.Date; @@ -102,81 +115,6 @@ export default function RRuleEteSync(props: PropsType) { return Ends.Never; } } - - function handleCheckboxWeekday(event: React.FormEvent<{ value: unknown }>): void { - const checkbox = event.target as HTMLInputElement; - const weekday = Number(checkbox.value); - let byweekdayArray = options.byday as WeekDay[]; - let byweekday; - if (!checkbox.checked && byweekdayArray) { - byweekday = byweekdayArray.filter((day) => day !== weekday); - } else if (byweekdayArray) { - byweekdayArray = byweekdayArray.filter((day) => day !== weekday); - byweekday = [...byweekdayArray, weekday]; - } else { - byweekday = [weekday]; - } - updateRule({ byday: byweekday }); - } - function handleCheckboxMonth(event: React.FormEvent<{ value: unknown }>): void { - const checkbox = event.target as HTMLInputElement; - const month = Number(checkbox.value); - let bymonthArray = options.bymonth as Months[]; - let bymonth; - if (!checkbox.checked && bymonthArray) { - bymonth = bymonthArray.filter((day) => day !== month); - } else if (bymonthArray) { - bymonthArray = bymonthArray.filter((day) => day !== month); - bymonth = [...bymonthArray, month]; - } else { - bymonth = [month]; - } - updateRule({ bymonth: bymonth }); - } - function isMonthChecked(month: number): boolean { - if (options.bymonth) { - return isNumber(options.bymonth.find((value) => Months[value] === Months[month])); - } else { - return false; - } - } - function isWeekdayChecked(day: number): boolean { - if (options.byday) { - return isNumber(options.byday.find((value) => WeekDay[value] === WeekDay[day])); - } else { - return false; - } - } - const checkboxMonths = months.map((month) => { - return ( - } - key={month} - label={Months[month]} /> - ); - }); - - const checkboxWeekDays = weekdays.map((day) => { - return ( - } - key={day} - label={WeekDay[day]} /> - ); - }); - return ( <>
@@ -204,14 +142,7 @@ export default function RRuleEteSync(props: PropsType) { style={{ alignSelf: 'flex-end', marginLeft: 20 }} onChange={(event: React.FormEvent<{ value: unknown }>) => { const freq = (event.target as HTMLSelectElement).value as Frequency; - const updatedOptions = { - freq: freq, - bysetpos: undefined, - bymonthday: freq === 'MONTHLY' || 'YEARLY' === freq ? [1] : undefined, - byweekday: undefined, - bymonth: freq === 'YEARLY' ? [Months.Jan] : undefined, - }; - updateRule(updatedOptions); + updateRule({ freq: freq }); }} > {menuItemsFrequency} @@ -265,34 +196,40 @@ export default function RRuleEteSync(props: PropsType) {
{options.freq === 'YEARLY' &&
- Months - + + Months + +
} {(options.freq && options.freq !== 'DAILY') &&
- Weekdays - + + Weekdays + +
} From c7c8b09e414608982bc61882efe017d45c15e5a7 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sun, 12 Jan 2020 19:09:31 +0200 Subject: [PATCH 08/20] widgets: RRule layout and styles --- src/widgets/RRule.tsx | 163 ++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 79 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 46b5997..e89de14 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -100,6 +100,10 @@ const menuItemsWeekDays = weekdays.map((day) => { {WeekDay[day]} ); }); +const styles = { + multiSelect: { minWidth: 120, maxWidth: '100%' }, + width: { width: 120 }, +}; export default function RRuleEteSync(props: PropsType) { const options = props.rrule; function updateRule(newOptions: Partial): void { @@ -148,25 +152,6 @@ export default function RRuleEteSync(props: PropsType) { {menuItemsFrequency}
- {options.bymonthday && - ) => { - event.preventDefault(); - const value = (event.currentTarget as HTMLInputElement).value; - const numberValue = Number(value); - if (value === '') { - updateRule({ bymonthday: undefined }); - } else if (numberValue < 32 && numberValue > 0) { - updateRule({ bymonthday: [numberValue] }); - } - }} - /> - } { !disableComplex &&
{(options.freq === 'MONTHLY') && @@ -193,26 +178,56 @@ export default function RRuleEteSync(props: PropsType) { Last }
} + + Ends + + + {options.until && + { + const value = date ? date : null; + updateRule({ until: ICAL.Time.fromJSDate(value, true) }); + }} + /> + } + {options.count && + ) => { + event.preventDefault(); + const inputNode = event.currentTarget as HTMLInputElement; + if (inputNode.value === '') { + updateRule({ count: 1 }); + } else if (inputNode.valueAsNumber) { + updateRule({ count: inputNode.valueAsNumber }); + } + }} + /> + }
- {options.freq === 'YEARLY' && -
- - Months - - -
- } {(options.freq && options.freq !== 'DAILY') &&
@@ -220,7 +235,7 @@ export default function RRuleEteSync(props: PropsType) { ) => { - const value = Number((event.target as HTMLSelectElement).value); - let updateOptions; - if (value === Ends.Date) { - updateOptions = { count: undefined, until: ICAL.Time.now() }; - } else if (value === Ends.After) { - updateOptions = { until: undefined, count: 1 }; - } else { - updateOptions = { count: undefined, until: undefined }; - } - updateRule(updateOptions); - }}> - {menuItemsEnds} - - - {options.until && - { - const value = date ? date : null; - updateRule({ until: ICAL.Time.fromJSDate(value, true) }); - }} - /> - } - {options.count && + {options.freq === 'MONTHLY' && ) => { event.preventDefault(); - const inputNode = event.currentTarget as HTMLInputElement; - if (inputNode.value === '') { - updateRule({ count: 1 }); - } else if (inputNode.valueAsNumber) { - updateRule({ count: inputNode.valueAsNumber }); + const value = (event.currentTarget as HTMLInputElement).value; + const numberValue = Number(value); + if (value === '') { + updateRule({ bymonthday: undefined }); + } else if (numberValue < 32 && numberValue > 0) { + updateRule({ bymonthday: [numberValue] }); } }} /> } + {options.freq === 'YEARLY' && +
+ + Months + + +
+ }
); From 0657e79e4529302abe51444874fbfed19d6d74d7 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sun, 12 Jan 2020 23:30:31 +0200 Subject: [PATCH 09/20] widgets: name change "RRuleEteSync" to "RRule" --- src/components/EventEdit.tsx | 5 ++--- src/widgets/RRule.tsx | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index a49d865..895ed86 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -33,7 +33,7 @@ import * as EteSync from 'etesync'; import { getCurrentTimezone } from '../helpers'; import { EventType, timezoneLoadFromName } from '../pim-types'; -import RRuleEteSync, { RRuleOptions } from '../widgets/RRule'; +import RRule, { RRuleOptions } from '../widgets/RRule'; interface PropsType { @@ -357,8 +357,7 @@ class EventEdit extends React.PureComponent { label="Recurring" /> - {this.state.rruleOptions && - diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index e89de14..902f0a6 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -104,7 +104,7 @@ const styles = { multiSelect: { minWidth: 120, maxWidth: '100%' }, width: { width: 120 }, }; -export default function RRuleEteSync(props: PropsType) { +export default function RRule(props: PropsType) { const options = props.rrule; function updateRule(newOptions: Partial): void { const updatedOptions = { ...options, ...newOptions }; From bd98d833aac2c629ad9610219b73eae67f254d99 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sun, 12 Jan 2020 23:23:55 +0200 Subject: [PATCH 10/20] components: EventEdit warning message --- src/components/EventEdit.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index 895ed86..b863aed 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -245,9 +245,7 @@ class EventEdit extends React.PureComponent { }, }; - if (this.props.item && this.props.item.isRecurring()) { - console.log(1); - } + const recurring = this.props.item && this.props.item.isRecurring(); const differentTimezone = this.state.timezone && (this.state.timezone !== getCurrentTimezone()) && timezoneLoadFromName(this.state.timezone); @@ -256,6 +254,13 @@ class EventEdit extends React.PureComponent {

{this.props.item ? 'Edit Event' : 'New Event'}

+ {recurring && ( +
+ IMPORTANT: + This is a recurring event, for now, only editing the whole series + (by editing the first instance) is supported. +
+ )} {this.state.error && (
ERROR! {this.state.error}
)} From 0661190aef6a28874ef5274e796bdfda955b056a Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sun, 12 Jan 2020 23:32:35 +0200 Subject: [PATCH 11/20] components: EventEdit state name "rruleOptions" to "rrule" --- src/components/EventEdit.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index b863aed..64da6b3 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -57,7 +57,7 @@ class EventEdit extends React.PureComponent { location: string; description: string; journalUid: string; - rruleOptions?: RRuleOptions; + rrule?: RRuleOptions; error?: string; showDeleteDialog: boolean; }; @@ -105,7 +105,7 @@ class EventEdit extends React.PureComponent { this.state.location = event.location ? event.location : ''; this.state.description = event.description ? event.description : ''; this.state.timezone = event.timezone; - this.state.rruleOptions = this.props.item?.component.getFirstPropertyValue('rrule'); + this.state.rrule = this.props.item?.component.getFirstPropertyValue('rrule'); } else { this.state.uid = uuid.v4(); } @@ -159,14 +159,13 @@ class EventEdit extends React.PureComponent { this.setState({ allDay: !this.state.allDay }); } public toggleRecurring() { - const value = this.state.rruleOptions ? undefined : { freq: 'WEEKLY', interval: 1 }; - this.setState({ rruleOptions: value }); + const value = this.state.rrule ? undefined : { freq: 'WEEKLY', interval: 1 }; + this.setState({ rrule: value }); } public handleRRuleChange(rrule: RRuleOptions): void { - this.setState({ rruleOptions: rrule }); - console.log(rrule); + this.setState({ rrule: rrule }); } public onSubmit(e: React.FormEvent) { e.preventDefault(); @@ -354,7 +353,7 @@ class EventEdit extends React.PureComponent { control={ @@ -362,9 +361,10 @@ class EventEdit extends React.PureComponent { label="Recurring" /> + {this.state.rrule && }
From 3897d38fb048d1fbfdc3d7bf97d834953744b732 Mon Sep 17 00:00:00 2001 From: Tal Leibman Date: Sun, 12 Jan 2020 23:34:10 +0200 Subject: [PATCH 12/20] components: EventEdit removed whitespace changes --- src/components/EventEdit.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index 64da6b3..bfd323a 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -57,6 +57,7 @@ class EventEdit extends React.PureComponent { location: string; description: string; journalUid: string; + rrule?: RRuleOptions; error?: string; showDeleteDialog: boolean; @@ -71,6 +72,7 @@ class EventEdit extends React.PureComponent { location: '', description: '', timezone: null, + journalUid: '', showDeleteDialog: false, }; @@ -203,6 +205,7 @@ class EventEdit extends React.PureComponent { : new EventType() ; + event.uid = this.state.uid; event.summary = this.state.title; event.startDate = startDate; @@ -404,7 +407,6 @@ class EventEdit extends React.PureComponent {
- Date: Tue, 14 Jan 2020 20:45:01 +0200 Subject: [PATCH 13/20] Event Edit: simplify code and make it persist the rrule While at it, also fix an issue with byday needing to be string[] and not number[] --- src/components/EventEdit.tsx | 8 ++++-- src/widgets/RRule.tsx | 53 +++++++----------------------------- 2 files changed, 15 insertions(+), 46 deletions(-) diff --git a/src/components/EventEdit.tsx b/src/components/EventEdit.tsx index bfd323a..3d1630e 100644 --- a/src/components/EventEdit.tsx +++ b/src/components/EventEdit.tsx @@ -54,11 +54,11 @@ class EventEdit extends React.PureComponent { start?: Date; end?: Date; timezone: string | null; + rrule?: RRuleOptions; location: string; description: string; journalUid: string; - rrule?: RRuleOptions; error?: string; showDeleteDialog: boolean; }; @@ -204,7 +204,7 @@ class EventEdit extends React.PureComponent { this.props.item.clone() : new EventType() - ; + ; event.uid = this.state.uid; event.summary = this.state.title; @@ -219,6 +219,9 @@ class EventEdit extends React.PureComponent { event.endDate = event.endDate?.convertToZone(timezone); } } + if (this.state.rrule) { + event.component.updatePropertyWithValue('rrule', new ICAL.Recur(this.state.rrule!)); + } event.component.updatePropertyWithValue('last-modified', ICAL.Time.now()); @@ -250,7 +253,6 @@ class EventEdit extends React.PureComponent { const recurring = this.props.item && this.props.item.isRecurring(); const differentTimezone = this.state.timezone && (this.state.timezone !== getCurrentTimezone()) && timezoneLoadFromName(this.state.timezone); - return ( <>

diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 902f0a6..97e29ef 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -7,24 +7,10 @@ interface PropsType { onChange: (rrule: RRuleOptions) => void; rrule: RRuleOptions; } -type Frequency = 'YEARLY' | 'MONTHLY' | 'WEEKLY' | 'DAILY' | 'HOURLY' | 'MINUTELY' | 'SECONDLY'; const disableComplex = true; -export interface RRuleOptions { - freq: Frequency; - interval?: number; - wkst?: WeekDay; - until?: ICAL.Time; - count?: number; - bysecond?: number[]; - byminute?: number[]; - byhour?: number[]; - byday?: number[]; - bymonthday?: number[]; - byyearday?: number[]; - byweekno?: number[]; - bymonth?: number[]; - bysetpos?: number[]; -} + +export type RRuleOptions = ICAL.RecurData; + enum Ends { Never, Date, @@ -57,34 +43,15 @@ enum WeekDay { Fr, Sa, } + const menuItemsEnds = [Ends.Never, Ends.Date, Ends.After].map((key) => { return ( {Ends[key]} ); }); -const weekdays = [ - WeekDay.Su, - WeekDay.Mo, - WeekDay.Tu, - WeekDay.We, - WeekDay.Th, - WeekDay.Fr, - WeekDay.Sa, -]; -const months = [ - Months.Jan, - Months.Feb, - Months.Mar, - Months.Apr, - Months.May, - Months.Jun, - Months.Jul, - Months.Aug, - Months.Sep, - Months.Oct, - Months.Nov, - Months.Dec, -]; +const weekdays: WeekDay[] = Array.from(Array(7)).map((_, i) => i + 1); +const months: Months[] = Array.from(Array(12)).map((_, i) => i + 1); + const menuItemsFrequency = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'].map((value) => { return ( {value.toLowerCase()} @@ -97,7 +64,7 @@ const menuItemMonths = months.map((month) => { }); const menuItemsWeekDays = weekdays.map((day) => { return ( - {WeekDay[day]} + {WeekDay[day]} ); }); const styles = { @@ -145,7 +112,7 @@ export default function RRule(props: PropsType) { value={options.freq} style={{ alignSelf: 'flex-end', marginLeft: 20 }} onChange={(event: React.FormEvent<{ value: unknown }>) => { - const freq = (event.target as HTMLSelectElement).value as Frequency; + const freq = (event.target as HTMLSelectElement).value as ICAL.FrequencyValues; updateRule({ freq: freq }); }} > @@ -239,7 +206,7 @@ export default function RRule(props: PropsType) { onChange={(event: React.ChangeEvent<{ value: unknown }>) => { const value = event.target.value as string[]; if (value) { - updateRule({ byday: value.map((day) => Number(day)) }); + updateRule({ byday: value }); } }}> {menuItemsWeekDays} From dfe5518f385b502d9b15d10d75fbd2089c54be8e Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Tue, 14 Jan 2020 21:03:38 +0200 Subject: [PATCH 14/20] RRule: improve design a bit --- src/widgets/RRule.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 97e29ef..f39ecae 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -90,10 +90,12 @@ export default function RRule(props: PropsType) { <>
@@ -245,9 +243,7 @@ export default function RRule(props: PropsType) { multiple onChange={(event: React.ChangeEvent<{ value: unknown }>) => { const value = event.target.value as string[]; - if (value) { - updateRule({ bymonth: value.map((month) => Number(month)) }); - } + updateRule({ bymonth: value.map((month) => Number(month)) }); }}> {menuItemMonths} From 16255e4cade8f3058f76243b2cdc9507980f8adb Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Tue, 14 Jan 2020 21:43:48 +0200 Subject: [PATCH 19/20] RRule: fix indentation. --- src/widgets/RRule.tsx | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/widgets/RRule.tsx b/src/widgets/RRule.tsx index 1200561..360de4b 100644 --- a/src/widgets/RRule.tsx +++ b/src/widgets/RRule.tsx @@ -121,8 +121,8 @@ export default function RRule(props: PropsType) { {menuItemsFrequency}
- { - !disableComplex &&
+ {!disableComplex && ( +
{(options.freq === 'MONTHLY') && } {options.bysetpos && - } -
} + + } + {options.bysetpos && + + } +
+ )} Ends