| |
@@ -1,12 +1,15 @@
|
| |
import { Injectable } from '@angular/core';
|
| |
- import 'rxjs/add/operator/map';
|
| |
|
| |
import * as _ from 'lodash';
|
| |
import * as moment from 'moment-timezone';
|
| |
|
| |
import { HttpClient } from '@angular/common/http';
|
| |
+ import { Storage } from '@ionic/storage';
|
| |
import { Observable } from 'rxjs/Observable';
|
| |
- import { chooseEndpoint } from '../../utils';
|
| |
+ import { fromPromise } from 'rxjs/observable/fromPromise';
|
| |
+ import { merge } from 'rxjs/observable/merge';
|
| |
+ import { tap, map } from 'rxjs/operators';
|
| |
+ import { chooseEndpoint, defaultValue } from '../../utils';
|
| |
|
| |
/**
|
| |
* FedoCal API endpoint
|
| |
@@ -16,6 +19,11 @@
|
| |
const ENDPOINT = chooseEndpoint('/fedocal', 'https://apps.fedoraproject.org/calendar/api');
|
| |
|
| |
/**
|
| |
+ * The key used for storing offline content
|
| |
+ */
|
| |
+ const CALENDAR_STORAGE_KEY = 'fedocal__calendars';
|
| |
+
|
| |
+ /**
|
| |
* Date format for display
|
| |
*/
|
| |
const DATE_FORMAT = 'dddd, MMMM Do YYYY';
|
| |
@@ -45,6 +53,29 @@
|
| |
}
|
| |
|
| |
/**
|
| |
+ * Get the key for offline storage which stores the meetings for the given Calendar
|
| |
+ *
|
| |
+ * @param calendar Calendar
|
| |
+ * @returns key to use while accessing offline content
|
| |
+ */
|
| |
+ function getCalendarStorageKey(calendar: Calendar): string {
|
| |
+ return `fedocal__meetings-${calendar.realName}`;
|
| |
+ }
|
| |
+
|
| |
+ /**
|
| |
+ * 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;
|
| |
+ }
|
| |
+
|
| |
+ /**
|
| |
* A calendar on FedoCal
|
| |
*
|
| |
* Calendars are assoicated with a group / SIG / or event. They contain a number
|
| |
@@ -144,7 +175,17 @@
|
| |
*/
|
| |
@Injectable()
|
| |
export class FedoCalService {
|
| |
- constructor(private http: HttpClient) {
|
| |
+
|
| |
+ constructor(private http: HttpClient, private storage: Storage) {
|
| |
+ }
|
| |
+
|
| |
+ /**
|
| |
+ * Fetch the list of calendars on FedoCal from offline cache
|
| |
+ *
|
| |
+ * @returns Observable which emits an array of posts
|
| |
+ */
|
| |
+ private loadCachedCalendars(): Observable<Calendar[]> {
|
| |
+ return fromPromise(this.storage.get(CALENDAR_STORAGE_KEY)).pipe(defaultValue([]));
|
| |
}
|
| |
|
| |
/**
|
| |
@@ -152,63 +193,94 @@
|
| |
*
|
| |
* @returns Observable which emits an array of calendars
|
| |
*/
|
| |
- getCalendars(): Observable<Calendar[]> {
|
| |
+ fetchCalendars(): 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'
|
| |
- })));
|
| |
+ .pipe(
|
| |
+ 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'
|
| |
+ })))
|
| |
+ );
|
| |
}
|
| |
|
| |
/**
|
| |
- * Fetch the list of meetings for a given FedoCal calendar name
|
| |
+ * Fetch the list of calendars from FedoCal
|
| |
+ *
|
| |
+ * Loads from offline cache first and then from API. The response of the API
|
| |
+ * request is persisted into the disk cache for further requests.
|
| |
+ *
|
| |
+ * @returns Observable which emits an array of calendars
|
| |
+ */
|
| |
+ getCalendars(): Observable<Calendar[]> {
|
| |
+ return merge(this.loadCachedCalendars(), this.fetchCalendars().pipe(
|
| |
+ tap(x => this.storage.set(CALENDAR_STORAGE_KEY, x))
|
| |
+ ));
|
| |
+ }
|
| |
+
|
| |
+ /**
|
| |
+ * Fetch the list of meetings for a given FedoCal calendar name from offline
|
| |
+ * cache
|
| |
*
|
| |
* @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,
|
| |
- };
|
| |
- }));
|
| |
+ private loadCachedMeetings(calendar: Calendar): Observable<Meeting[]> {
|
| |
+ return fromPromise(this.storage.get(getCalendarStorageKey(calendar))).pipe(defaultValue([]));
|
| |
}
|
| |
- }
|
| |
|
| |
- /**
|
| |
- * 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;
|
| |
+ /**
|
| |
+ * Fetch the list of meetings for a given FedoCal calendar name from FedoCal API
|
| |
+ *
|
| |
+ * @param calendar FedoCal calendar name
|
| |
+ * @returns Observable which emits an array of meetings
|
| |
+ */
|
| |
+ fetchMeetings(calendar: Calendar): Observable<Meeting[]> {
|
| |
+
|
| |
+ return this.http.get(`${ENDPOINT}/meetings/`, { params: { calendar: calendar.realName } })
|
| |
+ .pipe(
|
| |
+ 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,
|
| |
+ };
|
| |
+ }))
|
| |
+ );
|
| |
+ }
|
| |
+
|
| |
+ /**
|
| |
+ * Fetch the list of meetings for a given FedoCal calendar name
|
| |
+ *
|
| |
+ * Loads from offline cache first and then from API. The response of the API
|
| |
+ * request is persisted into the disk cache for further requests.
|
| |
+ *
|
| |
+ * @param calendar FedoCal calendar name
|
| |
+ * @returns Observable which emits an array of meetings
|
| |
+ */
|
| |
+ getMeetings(calendar: Calendar): Observable<Meeting[]> {
|
| |
+ return merge(this.loadCachedMeetings(calendar), this.fetchMeetings(calendar).pipe(
|
| |
+ tap(x => this.storage.set(getCalendarStorageKey(calendar), x))
|
| |
+ ));
|
| |
+ }
|
| |
}
|
| |
|
| |
Blocked by #57
Closes #62