Skip to content

Commit

Permalink
Events: Added "add to calendar" actions to each event item (#318)
Browse files Browse the repository at this point in the history
* set up logs to test new description parser

* Resolved bug in #309

* Started making progress on fix/281

* Added sliding effect and clipboard interaction

* Move copy to clipboard button to right side

* Update clipboard.svelte

* Renamed icon to CopyLinkIcon

* Made toast animation one dimensional

* Added "event event summary" action

* Added highlight effects to toasts per path

* Fixed lint error, unused import

* Added "event event summary" action

* Update event-item.svelte

* Added path metadata to toast notification

* Removed unused import

* Added copy google calendar button to action bar

* Update event-item.svelte

* Added Google, Office 365, Outlook, and Yahoo! calendar links

* Ran formatter

* Ran `npm audit fix`

* Forked [`calendar-link`](https://github.com/AnandChowdhary/calendar-link#readme)

* Update calendar-link.ts

* Update calendar-link.ts

* Uninstalled unused dependency calendar-link

* Removed bloaty action buttons

* moved calendar-link internal lib to lib root

* updated lint errors from removing action buttons

* Created separate summary and title properties in AcmEvent interface
  • Loading branch information
EthanThatOneKid authored Mar 5, 2022
1 parent 8a7f1a4 commit 6e751ac
Show file tree
Hide file tree
Showing 14 changed files with 632 additions and 149 deletions.
256 changes: 209 additions & 47 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1",
"axios": "^0.24.0",
"dayjs": "^1.10.8",
"discord.js": "^13.2.0",
"dotenv": "^10.0.0",
"eslint": "^7.32.0",
"eslint-plugin-svelte3": "^3.2.1",
"node-fetch": "^3.0.0",
"prettier": "^2.4.1",
"prettier-plugin-svelte": "^2.4.0",
"query-string": "^7.1.1",
"sass": "^1.42.1",
"size-limit": "^7.0.4",
"svelte": "^3.42.6",
Expand Down
6 changes: 6 additions & 0 deletions src/lib/calendar-link/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# `calendar-link`

## Credit

- Demo: <https://anandchowdhary.github.io/calendar-link/>
- Repository: <https://github.com/AnandChowdhary/calendar-link>
178 changes: 178 additions & 0 deletions src/lib/calendar-link/calendar-link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import { stringify } from 'query-string';

import type { CalendarEvent, NormalizedCalendarEvent, Google, Outlook, Yahoo } from './interfaces';
import { TimeFormats } from './utils';

dayjs.extend(utc);

function formatTimes(
{ startUtc, endUtc }: NormalizedCalendarEvent,
dateTimeFormat: keyof typeof TimeFormats
): { start: string; end: string } {
const format = TimeFormats[dateTimeFormat];
return { start: startUtc.format(format), end: endUtc.format(format) };
}

export function eventify(event: CalendarEvent): NormalizedCalendarEvent {
const { start, end, duration, ...rest } = event;
const startUtc = dayjs(start).utc();
const endUtc = end
? dayjs(end).utc()
: (() => {
if (event.allDay) {
return startUtc.add(1, 'day');
}
if (duration && duration.length == 2) {
const value = Number(duration[0]);
const unit = duration[1];
return startUtc.add(value, unit);
}
return dayjs().utc();
})();
return {
...rest,
startUtc,
endUtc,
};
}

export function google(calendarEvent: CalendarEvent): string {
const event = eventify(calendarEvent);
const { start, end } = formatTimes(event, event.allDay ? 'allDay' : 'dateTimeUTC');
const details: Google = {
action: 'TEMPLATE',
text: event.title,
details: event.description,
location: event.location,
trp: event.busy,
dates: start + '/' + end,
};
if (event.guests && event.guests.length) {
details.add = event.guests.join();
}
return `https://calendar.google.com/calendar/render?${stringify(details)}`;
}

export function outlook(calendarEvent: CalendarEvent): string {
const event = eventify(calendarEvent);
const { start, end } = formatTimes(event, 'dateTimeWithOffset');
const details: Outlook = {
path: '/calendar/action/compose',
rru: 'addevent',
startdt: start,
enddt: end,
subject: event.title,
body: event.description,
location: event.location,
allday: event.allDay || false,
};
return `https://outlook.live.com/calendar/0/deeplink/compose?${stringify(details)}`;
}

export function office365(calendarEvent: CalendarEvent): string {
const event = eventify(calendarEvent);
const { start, end } = formatTimes(event, 'dateTimeWithOffset');
const details: Outlook = {
path: '/calendar/action/compose',
rru: 'addevent',
startdt: start,
enddt: end,
subject: event.title,
body: event.description,
location: event.location,
allday: event.allDay || false,
};
return `https://outlook.office.com/calendar/0/deeplink/compose?${stringify(details)}`;
}

export function yahoo(calendarEvent: CalendarEvent): string {
const event = eventify(calendarEvent);
const { start, end } = formatTimes(event, event.allDay ? 'allDay' : 'dateTimeUTC');
const details: Yahoo = {
v: 60,
title: event.title,
st: start,
et: end,
desc: event.description,
in_loc: event.location,
dur: event.allDay ? 'allday' : false,
};
return `https://calendar.yahoo.com/?${stringify(details)}`;
}

export function ics(calendarEvent: CalendarEvent): string {
const event = eventify(calendarEvent);
const formattedDescription: string = (event.description || '')
.replace(/,/gm, ',')
.replace(/;/gm, ';')
.replace(/\n/gm, '\\n')
.replace(/(\\n)[\s\t]+/gm, '\\n');

const formattedLocation: string = (event.location || '')
.replace(/,/gm, ',')
.replace(/;/gm, ';')
.replace(/\n/gm, '\\n')
.replace(/(\\n)[\s\t]+/gm, '\\n');

const { start, end } = formatTimes(event, event.allDay ? 'allDay' : 'dateTimeUTC');
const calendarChunks = [
{
key: 'BEGIN',
value: 'VCALENDAR',
},
{
key: 'VERSION',
value: '2.0',
},
{
key: 'BEGIN',
value: 'VEVENT',
},
{
key: 'URL',
value: event.url,
},
{
key: 'DTSTART',
value: start,
},
{
key: 'DTEND',
value: end,
},
{
key: 'SUMMARY',
value: event.title,
},
{
key: 'DESCRIPTION',
value: formattedDescription,
},
{
key: 'LOCATION',
value: formattedLocation,
},
{
key: 'END',
value: 'VEVENT',
},
{
key: 'END',
value: 'VCALENDAR',
},
];

let calendarUrl = '';

calendarChunks.forEach((chunk) => {
if (chunk.value) {
calendarUrl += `${chunk.key}:${encodeURIComponent(`${chunk.value}\n`)}`;
}
});

return `data:text/calendar;charset=utf8,${calendarUrl}`;
}

export type { CalendarEvent };
1 change: 1 addition & 0 deletions src/lib/calendar-link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './calendar-link';
54 changes: 54 additions & 0 deletions src/lib/calendar-link/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type dayjs from 'dayjs';

interface CalendarEvent {
title: string;
start: Date;
end?: Date;
duration?: [number, dayjs.UnitType];
allDay?: boolean;
description?: string;
location?: string;
busy?: boolean;
guests?: string[];
url?: string;
}

interface NormalizedCalendarEvent extends Omit<CalendarEvent, 'start' | 'end' | 'duration'> {
startUtc: dayjs.Dayjs;
endUtc: dayjs.Dayjs;
}

interface Google extends Record<string, string | boolean | number | undefined> {
action: string;
text: string;
dates: string;
details?: string;
location?: string;
trp?: boolean;
sprop?: string;
add?: string;
src?: string;
recur?: string;
}

interface Outlook extends Record<string, string | boolean | number | undefined> {
path: string;
rru: string;
startdt: string;
enddt: string;
subject: string;
allday?: boolean;
body?: string;
location?: string;
}

interface Yahoo extends Record<string, string | boolean | number | undefined> {
v: number;
title: string;
st: string;
et: string;
desc?: string;
in_loc?: string;
}

export type { CalendarEvent, NormalizedCalendarEvent, Outlook, Yahoo, Google };
5 changes: 5 additions & 0 deletions src/lib/calendar-link/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const TimeFormats = {
dateTimeWithOffset: 'YYYY-MM-DD[T]HH:mm:ssZ',
dateTimeUTC: 'YYYYMMDD[T]HHmmss[Z]',
allDay: 'YYYYMMDD',
};
Loading

1 comment on commit 6e751ac

@vercel
Copy link

@vercel vercel bot commented on 6e751ac Mar 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.