Skip to content

Commit

Permalink
feat: Ensure Calendar is renderable all the time
Browse files Browse the repository at this point in the history
This commit changes the behavior of `ical-generator` so that `ICalCalendar` objects can be serialized with `toString()` at any time, as long as the types are adhered to. This leads to some breaking changes.

close #344

BREAKING CHANGE: `Alarm.trigger` now defaults to 10min before event, `Alarm.type` now defaults to `display`, `Alarm.interval()` got removed, use `Alarm.repeat()` instead, `Alarm.repeat()` now gives/takes an object instead of a number, `Attendee.email` can’t be `null | undefined`, `Category.name` can’t be `null | undefined`, `Event.start` now defaults to now (`new Date()`). For details and examples checkout the migration guide at https://github.com/sebbo2002/ical-generator/wiki/Migration-Guide:-v5-%E2%86%92-v6
  • Loading branch information
sebbo2002 committed Oct 19, 2023
1 parent 27332b7 commit f1328a3
Show file tree
Hide file tree
Showing 13 changed files with 866 additions and 605 deletions.
181 changes: 68 additions & 113 deletions src/alarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,35 +32,45 @@ export interface ICalAttachment {
mime: string | null;
}

export interface ICalAlarmData {
type?: ICalAlarmType | null;
trigger?: number | ICalDateTimeValue | null;
export type ICalAlarmData = ICalAlarmBaseData |
ICalAlarmTriggerData |
ICalAlarmTriggerAfterData |
ICalAlarmTriggerBeforeData;

type ICalAlarmTriggerData = ICalAlarmBaseData & { trigger: number | ICalDateTimeValue };
type ICalAlarmTriggerAfterData = ICalAlarmBaseData & { triggerAfter: number | ICalDateTimeValue };
type ICalAlarmTriggerBeforeData = ICalAlarmBaseData & { triggerBefore: number | ICalDateTimeValue };

interface ICalAlarmBaseData {
type?: ICalAlarmType;
relatesTo?: ICalAlarmRelatesTo | null;
triggerBefore?: number | ICalDateTimeValue | null;
triggerAfter?: number | ICalDateTimeValue | null;
repeat?: number | null;
interval?: number | null;
repeat?: ICalAlarmRepeatData | null;
attach?: string | ICalAttachment | null;
description?: string | null;
x?: {key: string, value: string}[] | [string, string][] | Record<string, string>;
}

export interface ICalAlarmRepeatData {
times: number;
interval: number;
}

interface ICalInternalAlarmData {
type: ICalAlarmType | null;
trigger: ICalDateTimeValue | number | null;
type: ICalAlarmType;
trigger: ICalDateTimeValue | number;
relatesTo: ICalAlarmRelatesTo | null;
repeat: number | null;
repeat: ICalAlarmRepeatData | null;
interval: number | null;
attach: ICalAttachment | null;
description: string | null;
x: [string, string][];
}

export interface ICalAlarmJSONData {
type: ICalAlarmType | null;
trigger: string | number | null;
type: ICalAlarmType;
trigger: string | number;
relatesTo: ICalAlarmRelatesTo | null;
repeat: number | null;
repeat: ICalAlarmRepeatData | null;
interval: number | null;
attach: ICalAttachment | null;
description: string | null;
Expand Down Expand Up @@ -99,8 +109,8 @@ export default class ICalAlarm {
*/
constructor (data: ICalAlarmData, event: ICalEvent) {
this.data = {
type: null,
trigger: null,
type: ICalAlarmType.display,
trigger: -600,
relatesTo: null,
repeat: null,
interval: null,
Expand All @@ -115,11 +125,10 @@ export default class ICalAlarm {
}

data.type !== undefined && this.type(data.type);
data.trigger !== undefined && this.trigger(data.trigger);
data.triggerBefore !== undefined && this.triggerBefore(data.triggerBefore);
data.triggerAfter !== undefined && this.triggerAfter(data.triggerAfter);
data.repeat !== undefined && this.repeat(data.repeat);
data.interval !== undefined && this.interval(data.interval);
'trigger' in data && data.trigger !== undefined && this.trigger(data.trigger);
'triggerBefore' in data && data.triggerBefore !== undefined && this.triggerBefore(data.triggerBefore);
'triggerAfter' in data && data.triggerAfter !== undefined && this.triggerAfter(data.triggerAfter);
data.repeat && this.repeat(data.repeat);
data.attach !== undefined && this.attach(data.attach);
data.description !== undefined && this.description(data.description);
data.x !== undefined && this.x(data.x);
Expand All @@ -130,24 +139,19 @@ export default class ICalAlarm {
* Get the alarm type
* @since 0.2.1
*/
type (type: ICalAlarmType | null): this;
type (type: ICalAlarmType): this;

/**
* Set the alarm type. See [[`ICalAlarmType`]]
* for available status options.
* @since 0.2.1
*/
type (): ICalAlarmType | null;
type (type?: ICalAlarmType | null): this | ICalAlarmType | null {
type (): ICalAlarmType;
type (type?: ICalAlarmType): this | ICalAlarmType {
if (type === undefined) {
return this.data.type;
}
if (!type) {
this.data.type = null;
return this;
}

if (!Object.keys(ICalAlarmType).includes(type)) {
if (!type || !Object.keys(ICalAlarmType).includes(type)) {
throw new Error('`type` is not correct, must be either `display` or `audio`!');
}

Expand All @@ -165,7 +169,7 @@ export default class ICalAlarm {
*
* @since 0.2.1
*/
trigger (): number | ICalDateTimeValue | null;
trigger (): number | ICalDateTimeValue;

/**
* Use this method to set the alarm time.
Expand All @@ -185,28 +189,22 @@ export default class ICalAlarm {
*
* @since 0.2.1
*/
trigger (trigger: number | ICalDateTimeValue | Date | null): this;
trigger (trigger?: number | ICalDateTimeValue | Date | null): this | number | ICalDateTimeValue | null {
trigger (trigger: number | ICalDateTimeValue | Date): this;
trigger (trigger?: number | ICalDateTimeValue | Date): this | number | ICalDateTimeValue {

// Getter
if (trigger === undefined && typeof this.data.trigger === 'number') {
return -1 * this.data.trigger;
}
if (trigger === undefined && this.data.trigger) {
return this.data.trigger;
}
if (trigger === undefined) {
return null;
return this.data.trigger;
}

// Setter
if (!trigger) {
this.data.trigger = null;
}
else if (typeof trigger === 'number' && isFinite(trigger)) {
if (typeof trigger === 'number' && isFinite(trigger)) {
this.data.trigger = -1 * trigger;
}
else if(typeof trigger === 'number') {
else if(!trigger || typeof trigger === 'number') {
throw new Error('`trigger` is not correct, must be a finite number or a supported date!');
}
else {
Expand Down Expand Up @@ -275,7 +273,7 @@ export default class ICalAlarm {
*
* @since 0.2.1
*/
triggerAfter (): number | ICalDateTimeValue | null;
triggerAfter (): number | ICalDateTimeValue;

/**
* Use this method to set the alarm time. Unlike `trigger`, this time
Expand All @@ -295,8 +293,8 @@ export default class ICalAlarm {
*
* @since 0.2.1
*/
triggerAfter (trigger: number | ICalDateTimeValue | null): this;
triggerAfter (trigger?: number | ICalDateTimeValue | null): this | number | ICalDateTimeValue | null {
triggerAfter (trigger: number | ICalDateTimeValue): this;
triggerAfter (trigger?: number | ICalDateTimeValue): this | number | ICalDateTimeValue {
if (trigger === undefined) {
return this.data.trigger;
}
Expand All @@ -315,7 +313,7 @@ export default class ICalAlarm {
* @since 0.2.1
* @alias trigger
*/
triggerBefore (trigger: number | ICalDateTimeValue | null): this;
triggerBefore (trigger: number | ICalDateTimeValue): this;

/**
* Use this method to set the alarm time.
Expand All @@ -336,8 +334,8 @@ export default class ICalAlarm {
* @since 0.2.1
* @alias trigger
*/
triggerBefore (): number | ICalDateTimeValue | null;
triggerBefore (trigger?: number | ICalDateTimeValue | null): this | number | ICalDateTimeValue | null {
triggerBefore (): number | ICalDateTimeValue;
triggerBefore (trigger?: number | ICalDateTimeValue): this | number | ICalDateTimeValue {
if(trigger === undefined) {
return this.trigger();
}
Expand All @@ -350,7 +348,7 @@ export default class ICalAlarm {
* Get Alarm Repetitions
* @since 0.2.1
*/
repeat(): number | null;
repeat(): ICalAlarmRepeatData | null;

/**
* Set Alarm Repetitions. Use this to repeat the alarm.
Expand All @@ -361,15 +359,17 @@ export default class ICalAlarm {
*
* // repeat the alarm 4 times every 5 minutes…
* cal.createAlarm({
* repeat: 4,
* interval: 300
* repeat: {
* times: 4,
* interval: 300
* }
* });
* ```
*
* @since 0.2.1
*/
repeat(repeat: number | null): this;
repeat (repeat?: number | null): this | number | null {
repeat(repeat: ICalAlarmRepeatData): this;
repeat (repeat?: ICalAlarmRepeatData | null): this | ICalAlarmRepeatData | null {
if (repeat === undefined) {
return this.data.repeat;
}
Expand All @@ -378,52 +378,17 @@ export default class ICalAlarm {
return this;
}

if (typeof repeat !== 'number' || !isFinite(repeat)) {
throw new Error('`repeat` is not correct, must be numeric!');
if (typeof repeat !== 'object') {
throw new Error('`repeat` is not correct, must be an object!');
}

this.data.repeat = repeat;
return this;
}


/**
* Get Repeat Interval
* @since 0.2.1
*/
interval (interval: number | null): this;

/**
* Set Repeat Interval
*
* ```javascript
* const cal = ical();
* const event = cal.createEvent();
*
* // repeat the alarm 4 times every 5 minutes…
* cal.createAlarm({
* repeat: 4,
* interval: 300
* });
* ```
*
* @since 0.2.1
*/
interval(): number | null;
interval (interval?: number | null): this | number | null {
if (interval === undefined) {
return this.data.interval || null;
if (typeof repeat.times !== 'number' || !isFinite(repeat.times)) {
throw new Error('`repeat.times` is not correct, must be numeric!');
}
if (!interval) {
this.data.interval = null;
return this;
if (typeof repeat.interval !== 'number' || !isFinite(repeat.interval)) {
throw new Error('`repeat.interval` is not correct, must be numeric!');
}

if (typeof interval !== 'number' || !isFinite(interval)) {
throw new Error('`interval` is not correct, must be numeric!');
}

this.data.interval = interval;
this.data.repeat = repeat;
return this;
}

Expand Down Expand Up @@ -619,13 +584,6 @@ export default class ICalAlarm {
toString (): string {
let g = 'BEGIN:VALARM\r\n';

if (!this.data.type) {
throw new Error('No value for `type` in ICalAlarm given!');
}
if (!this.data.trigger) {
throw new Error('No value for `trigger` in ICalAlarm given!');
}

// ACTION
g += 'ACTION:' + this.data.type.toUpperCase() + '\r\n';

Expand All @@ -638,26 +596,23 @@ export default class ICalAlarm {
}
}
else if (typeof this.data.trigger === 'number') {
g += 'TRIGGER;RELATED=' + this.data.relatesTo.toUpperCase() + ':' + toDurationString(this.data.trigger) + '\r\n';
g += 'TRIGGER;RELATED=' + this.data.relatesTo?.toUpperCase() + ':' + toDurationString(this.data.trigger) + '\r\n';
}
else {
g += 'TRIGGER;VALUE=DATE-TIME:' + formatDate(this.event.timezone(), this.data.trigger) + '\r\n';
}

// REPEAT
if (this.data.repeat && !this.data.interval) {
throw new Error('No value for `interval` in ICalAlarm given, but required for `repeat`!');
}
if (this.data.repeat) {
g += 'REPEAT:' + this.data.repeat + '\r\n';
}
if (!this.data.repeat.times) {
throw new Error('No value for `repeat.times` in ICalAlarm given, but required for `interval`!');
}
if (!this.data.repeat.interval) {
throw new Error('No value for `repeat.interval` in ICalAlarm given, but required for `repeat`!');
}

// INTERVAL
if (this.data.interval && !this.data.repeat) {
throw new Error('No value for `repeat` in ICalAlarm given, but required for `interval`!');
}
if (this.data.interval) {
g += 'DURATION:' + toDurationString(this.data.interval) + '\r\n';
g += 'REPEAT:' + this.data.repeat.times + '\r\n';
g += 'DURATION:' + toDurationString(this.data.repeat.interval) + '\r\n';
}

// ATTACH
Expand Down
Loading

0 comments on commit f1328a3

Please sign in to comment.