| |
@@ -4,23 +4,37 @@
|
| |
import * as _ from 'lodash';
|
| |
import * as moment from 'moment-timezone';
|
| |
|
| |
- import { Request } from '../request/request';
|
| |
-
|
| |
- const API_ENDPOINT = 'https://apps.fedoraproject.org/calendar/api/';
|
| |
- const API = {
|
| |
- base: [API_ENDPOINT],
|
| |
- calendars: [API_ENDPOINT, 'calendars'],
|
| |
- meetings: [API_ENDPOINT, 'meetings'],
|
| |
- };
|
| |
-
|
| |
+ import { HttpClient } from '@angular/common/http';
|
| |
+ import { Observable } from 'rxjs/Observable';
|
| |
+ import { chooseEndpoint } from '../../utils';
|
| |
+
|
| |
+ /**
|
| |
+ * FedoCal API endpoint
|
| |
+ *
|
| |
+ * It does not support CORS so we have to proxy it through Ionic CLI during development
|
| |
+ */
|
| |
+ const ENDPOINT = chooseEndpoint('/fedocal', 'https://apps.fedoraproject.org/calendar/api');
|
| |
+
|
| |
+ /**
|
| |
+ * Date format for display
|
| |
+ */
|
| |
const DATE_FORMAT = 'dddd, MMMM Do YYYY';
|
| |
- const TIME_FORMAT = 'h:mm a z';
|
| |
|
| |
- /*
|
| |
- Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n
|
| |
- */
|
| |
- function calendarNameToDisplayName(name:string) :string {
|
| |
- switch(name) {
|
| |
+ /**
|
| |
+ * Time format for display
|
| |
+ */
|
| |
+ const TIME_FORMAT = 'h:mm A z';
|
| |
+
|
| |
+ /**
|
| |
+ * Convert calendar name from API to a value suitable for display
|
| |
+ *
|
| |
+ * Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n
|
| |
+ *
|
| |
+ * @param name Calendar name as in API
|
| |
+ * @returns Friendly representation of the name
|
| |
+ */
|
| |
+ function calendarNameToDisplayName(name: string): string {
|
| |
+ switch (name) {
|
| |
case 'i18n':
|
| |
return 'i18n';
|
| |
case 'fesco':
|
| |
@@ -28,93 +42,173 @@
|
| |
default:
|
| |
return /^[A-Z][^\d-]*.*$/.test(name) ? name : _.startCase(name);
|
| |
}
|
| |
+ }
|
| |
|
| |
+ /**
|
| |
+ * A calendar on FedoCal
|
| |
+ *
|
| |
+ * Calendars are assoicated with a group / SIG / or event. They contain a number
|
| |
+ * of recurring meetings or events.
|
| |
+ */
|
| |
+ export interface Calendar {
|
| |
+
|
| |
+ /**
|
| |
+ * Name of calendar, as expressed in the API
|
| |
+ *
|
| |
+ * Serves as an ID for API calls.
|
| |
+ */
|
| |
+ realName: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Human friendly calendar name obtained from `realName`
|
| |
+ */
|
| |
+ displayName: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Calendar's description
|
| |
+ */
|
| |
+ description: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Group with admin previlages for this calendar
|
| |
+ */
|
| |
+ adminGroup: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Group with edit previlages for this calendar
|
| |
+ */
|
| |
+ editorGroup: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Contact person for this calendar
|
| |
+ */
|
| |
+ contact: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Whether the calendar is enabled
|
| |
+ */
|
| |
+ enabled: boolean,
|
| |
}
|
| |
|
| |
- /*
|
| |
- Generated class for the FedoCal provider.
|
| |
+ /**
|
| |
+ * A meeting on a FedoCal calendar
|
| |
+ */
|
| |
+ export interface Meeting {
|
| |
+
|
| |
+ /**
|
| |
+ * Name of the meeting
|
| |
+ */
|
| |
+ name: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Description of the meeting
|
| |
+ */
|
| |
+ description: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Where is the meeting happening?
|
| |
+ *
|
| |
+ * Can be an IRC channel or even a physical location.
|
| |
+ */
|
| |
+ location: string,
|
| |
+
|
| |
+ /**
|
| |
+ * Meeting start time
|
| |
+ */
|
| |
+ time: Date,
|
| |
+
|
| |
+ /**
|
| |
+ * Meeting end time
|
| |
+ */
|
| |
+ timeEnd: Date,
|
| |
+
|
| |
+ /**
|
| |
+ * Start time formatted for display
|
| |
+ */
|
| |
+ displayTime: {
|
| |
+ dateString: string,
|
| |
+ timeString: string,
|
| |
+ },
|
| |
+
|
| |
+ /**
|
| |
+ * End time formatted for display
|
| |
+ */
|
| |
+ displayTimeEnd: {
|
| |
+ dateString: string,
|
| |
+ timeString: string,
|
| |
+ }
|
| |
+ }
|
| |
|
| |
- See https://angular.io/docs/ts/latest/guide/dependency-injection.html
|
| |
- for more info on providers and Angular 2 DI.
|
| |
- */
|
| |
+ /**
|
| |
+ * Service for FedoCal
|
| |
+ */
|
| |
@Injectable()
|
| |
- export class FedoCal {
|
| |
- private calendars:any;
|
| |
- private meetings:any;
|
| |
-
|
| |
- constructor(private request:Request) {
|
| |
- this.calendars = [];
|
| |
- this.meetings = [];
|
| |
+ export class FedoCalService {
|
| |
+ constructor(private http: HttpClient) {
|
| |
}
|
| |
|
| |
- getCalendars() {
|
| |
- if (!_.isEmpty(this.calendars)) {
|
| |
- return Promise.resolve(this.calendars);
|
| |
- }
|
| |
-
|
| |
- return new Promise((resolve, reject) => {
|
| |
- this.request.get(API.calendars)
|
| |
- .then((data:any) => {
|
| |
- this.calendars = _.map(data.calendars, c => {
|
| |
- return {
|
| |
- real_name: c.calendar_name,
|
| |
- display_name: calendarNameToDisplayName(c.calendar_name),
|
| |
- description: c.calendar_description,
|
| |
- contact: c.calendar_contact,
|
| |
- };
|
| |
- });
|
| |
-
|
| |
- return resolve(this.calendars);
|
| |
- }).catch(reject);
|
| |
- });
|
| |
+ /**
|
| |
+ * Fetch the list of calendars from FedoCal API
|
| |
+ *
|
| |
+ * @returns Observable which emits an array of calendars
|
| |
+ */
|
| |
+ getCalendars(): Observable<Calendar[]> {
|
| |
+ return this.http.get(`${ENDPOINT}/calendars/`)
|
| |
+ .map((data: any) =>
|
| |
+ data.calendars.map((c: any) => ({
|
| |
+ realName: c.calendar_name,
|
| |
+ displayName: calendarNameToDisplayName(c.calendar_name),
|
| |
+ description: c.calendar_description,
|
| |
+ contact: c.calendar_contact,
|
| |
+ adminGroup: c.calendar_admin_group,
|
| |
+ editorGroup: c.calendar_editor_group,
|
| |
+ enabled: c.calendar_status === 'Enabled'
|
| |
+ })));
|
| |
}
|
| |
|
| |
- getMeetings(calendar) {
|
| |
- if (!_.isEmpty(this.meetings[calendar])) {
|
| |
- return Promise.resolve(this.meetings[calendar]);
|
| |
- }
|
| |
-
|
| |
- return new Promise((resolve, reject) => {
|
| |
- this.request.get(API.meetings, { calendar: calendar })
|
| |
- .then((data:any) => {
|
| |
- this.meetings[calendar] = _.map(data.meetings, m => {
|
| |
- const meeting:any = {
|
| |
- name: m.meeting_name,
|
| |
- real_description: m.meeting_information,
|
| |
- display_description: _.truncate(
|
| |
- m.meeting_information,
|
| |
- { length: 120, separator: ' ' }
|
| |
- ),
|
| |
- date_start: m.meeting_date,
|
| |
- time_start: m.meeting_time_start,
|
| |
- date_end: m.meeting_date_end,
|
| |
- time_end: m.meeting_time_end,
|
| |
- timezone: m.meeting_timezone,
|
| |
- location: m.meeting_location,
|
| |
- };
|
| |
-
|
| |
- const start = dateToMoment(meeting.date_start, meeting.time_start, meeting.timezone);
|
| |
- meeting.moment_start = start;
|
| |
- meeting.datetime_start = start.toDate();
|
| |
-
|
| |
- const end = dateToMoment(meeting.date_end, meeting.time_end, meeting.timezone);
|
| |
- meeting.moment_end = end;
|
| |
- meeting.datetime_end = end.toDate();
|
| |
-
|
| |
- meeting.display_date = start.format(DATE_FORMAT);
|
| |
- meeting.display_time = start.format(TIME_FORMAT);
|
| |
-
|
| |
- return meeting;
|
| |
- });
|
| |
-
|
| |
- return resolve(this.meetings[calendar]);
|
| |
- }).catch(reject);
|
| |
- });
|
| |
+ /**
|
| |
+ * Fetch the list of meetings for a given FedoCal calendar name
|
| |
+ *
|
| |
+ * @param calendar FedoCal calendar name
|
| |
+ * @returns Observable which emits an array of meetings
|
| |
+ */
|
| |
+ getMeetings(calendar:string): Observable<Meeting[]> {
|
| |
+
|
| |
+ return this.http.get(`${ENDPOINT}/meetings/`, { params: { calendar: calendar } })
|
| |
+ .map((data: any) => data.meetings.map(m => {
|
| |
+ const mTime = dateToMoment(m.meeting_date, m.meeting_time_start, m.meeting_timezone);
|
| |
+ const mTimeEnd = dateToMoment(m.meeting_date_end, m.meeting_time_stop, m.meeting_timezone);
|
| |
+
|
| |
+ return {
|
| |
+ name: m.meeting_name,
|
| |
+ description: m.meeting_information,
|
| |
+ time: mTime.toDate(),
|
| |
+ timeEnd: mTimeEnd.toDate(),
|
| |
+ displayTime: {
|
| |
+ // Format momentjs object to the defined format for display
|
| |
+ dateString: mTime.format(DATE_FORMAT),
|
| |
+ timeString: mTime.format(TIME_FORMAT)
|
| |
+ },
|
| |
+ displayTimeEnd: {
|
| |
+ dateString: mTimeEnd.format(DATE_FORMAT),
|
| |
+ timeString: mTimeEnd.format(TIME_FORMAT),
|
| |
+ },
|
| |
+ location: m.meeting_location,
|
| |
+ };
|
| |
+ }));
|
| |
}
|
| |
}
|
| |
|
| |
-
|
| |
- function dateToMoment(date, time, timezone) {
|
| |
- return moment.tz(date + 'T' + time + 'Z', timezone).tz('Etc/UTC');
|
| |
+ /**
|
| |
+ * Convert a date consisting of date, time and timezone as different strings to
|
| |
+ * a single momentjs date
|
| |
+ *
|
| |
+ * @param date Date string
|
| |
+ * @param time Time string
|
| |
+ * @param timezone Timezone identifier
|
| |
+ */
|
| |
+ function dateToMoment(date:string, time:string, timezone:string) {
|
| |
+ const m = moment.tz(`${date} ${time}`, timezone).tz('UTC');
|
| |
+ return m;
|
| |
}
|
| |
-
|
| |
+
|
| |
Shouldnt it be containing all the pages ?