From 824f8c35ef8d43c4bbfdc89ed068799e37079c9b Mon Sep 17 00:00:00 2001
From: Amitosh Swain Mahapatra <amitosh.swain@gmail.com>
Date: May 30 2018 07:00:27 +0000
Subject: [PATCH 1/4] Add type declarations to Http and Social providers


---

diff --git a/package-lock.json b/package-lock.json
index 719245a..90db4c2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4123,6 +4123,11 @@
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
       "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
     },
+    "lodash-es": {
+      "version": "4.17.10",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
+      "integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
+    },
     "lodash.assign": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
diff --git a/package.json b/package.json
index c529928..eea4ccf 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
     "ionic-plugin-keyboard": "^2.2.1",
     "ionicons": "^3.0.0",
     "lodash": "^4.14.0",
+    "lodash-es": "^4.17.10",
     "moment": "^2.14.1",
     "moment-timezone": "^0.5.5",
     "query-string": "^4.2.2",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index b5f9309..1378f59 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -18,8 +18,10 @@ import { SocialPage } from '../pages/social/social';
 import { WomenPage } from '../pages/women/women';
 
 import { Browser } from '../providers/browser/browser';
-import { Request } from '../providers/request/request';
+import { Http } from '../providers/http/http';
 import { IonicConfig } from '../providers/ionic-config/ionic-config';
+import { FacebookProvider } from '../providers/social/facebook';
+import { TwitterProvider } from '../providers/social/twitter';
 
 @NgModule({
   declarations: [
@@ -50,13 +52,15 @@ import { IonicConfig } from '../providers/ionic-config/ionic-config';
     Browser,
     IonicConfig,
     InAppBrowser,
-    Request,
+    Http,
     SocialSharing,
     SplashScreen,
     SpinnerDialog,
     StatusBar,
     Toast,
-    { provide: ErrorHandler, useClass: IonicErrorHandler }
+    FacebookProvider,
+    TwitterProvider,
+    { provide: ErrorHandler, useClass: IonicErrorHandler },
   ]
 })
 export class AppModule { }
diff --git a/src/pages/ask/ask.ts b/src/pages/ask/ask.ts
index 95433d0..7cf52af 100644
--- a/src/pages/ask/ask.ts
+++ b/src/pages/ask/ask.ts
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
 import { SocialSharing } from '@ionic-native/social-sharing';
 
 import { Browser } from '../../providers/browser/browser';
-import { AskFedora } from '../../providers/ask-fedora/ask-fedora';
+import { AskFedoraService } from '../../providers/ask-fedora/ask-fedora';
 
 /*
   Generated class for the AskPage page.
@@ -12,13 +12,13 @@ import { AskFedora } from '../../providers/ask-fedora/ask-fedora';
 */
 @Component({
   templateUrl: 'ask.html',
-  providers: [Browser, AskFedora],
+  providers: [Browser, AskFedoraService],
 })
 export class AskPage {
   private questions: any;
 
   constructor(private browser: Browser,
-    private askFedora: AskFedora, private socialSharing: SocialSharing) {
+    private askFedora: AskFedoraService, private socialSharing: SocialSharing) {
     this.questions = [];
   }
 
diff --git a/src/pages/social/social.ts b/src/pages/social/social.ts
index 7219971..0b30a6f 100644
--- a/src/pages/social/social.ts
+++ b/src/pages/social/social.ts
@@ -2,8 +2,9 @@ import { Component } from '@angular/core';
 import { SocialSharing } from '@ionic-native/social-sharing';
 
 import { Browser } from '../../providers/browser/browser';
-import { FB } from '../../providers/fb/fb';
-import { Tw } from '../../providers/tw/tw';
+import { FacebookProvider } from '../../providers/social/facebook';
+import { TwitterProvider } from '../../providers/social/twitter';
+import { Post } from '../../providers/social/social';
 
 /*
   Generated class for the SocialPage page.
@@ -13,18 +14,16 @@ import { Tw } from '../../providers/tw/tw';
 */
 @Component({
   templateUrl: 'social.html',
-  providers: [FB, Tw],
+  providers: [FacebookProvider, TwitterProvider],
 })
 export class SocialPage {
 
-  // TODO: Refactor, we are not supposed to use `any`, rather, ww should define an
-  // interface Post { content:string, date:Date }
-  private posts:Array<any>;
-  private tweets:Array<any>;
+  private posts:Post[];
+  private tweets:Post[];
   private updates:Array<any>;
   private USER:any;
 
-  constructor(private browser:Browser, private fb:FB, private tw:Tw,
+  constructor(private browser:Browser, private fb:FacebookProvider, private tw:TwitterProvider,
               private socialSharing:SocialSharing) {
     this.posts = [];
     this.tweets = [];
@@ -42,14 +41,14 @@ export class SocialPage {
 
   updateUpdates() {
     this.fb
-      .getPagePosts(this.USER.FB)
+      .getPosts(this.USER.FB)
       .then((posts:Array<any>) => {
         this.posts = posts;
         this.mergeUpdates();
       });
 
     this.tw
-      .getTimelineTweets(this.USER.TW)
+      .getPosts(this.USER.TW)
       .then((tweets:Array<any>) => {
         this.tweets = tweets;
         this.mergeUpdates();
diff --git a/src/providers/ask-fedora/ask-fedora.ts b/src/providers/ask-fedora/ask-fedora.ts
index 7929f70..f32e1cf 100644
--- a/src/providers/ask-fedora/ask-fedora.ts
+++ b/src/providers/ask-fedora/ask-fedora.ts
@@ -1,12 +1,23 @@
 import { Injectable } from '@angular/core';
 import 'rxjs/add/operator/map';
 
-import * as _ from 'lodash';
-
-import { Request } from '../request/request';
+import { Http } from '../http/http';
 
 const API_ENDPOINT = 'https://ask.fedoraproject.org/en/api/v1';
 
+export interface Question {
+  id: string,
+  title: string,
+  link: string,
+  answers: number,
+  content: string,
+  timestamp: Date,
+  tags: string[],
+  view: number,
+  vote: number,
+}
+
+
 /*
   Generated class for the AskFedora provider.
 
@@ -14,11 +25,11 @@ const API_ENDPOINT = 'https://ask.fedoraproject.org/en/api/v1';
   for more info on providers and Angular 2 DI.
 */
 @Injectable()
-export class AskFedora {
-  private API:any;
-  private questions:Array<any>;
+export class AskFedoraService {
+  private API: any;
+  private questions: Question[];
 
-  constructor(private request:Request) {
+  constructor(private request: Http) {
     this.API = {
       base: [API_ENDPOINT],
       questions: [API_ENDPOINT, 'questions'],
@@ -28,30 +39,29 @@ export class AskFedora {
   }
 
   getQuestions() {
-    if (!_.isEmpty(this.questions)) {
+    if (this.questions.length) {
       return Promise.resolve(this.questions);
     }
 
-    return new Promise((resolve, reject) => {
-      this.request.get(this.API.questions)
-        .then((data:any) => {
-          this.questions = _.map(data.questions, q => {
-            const question = {
-              id: q.id,
-              title: q.title,
-              link: q.url,
-              answers: q.answer_count,
-              content: q.summary,
-              timestamp: q.added_at,
-              tags:q.tags,
-              view:q.view_count,
-              vote:q.score,
-            };
-            return question;
-          });
-
-          return resolve(this.questions);
-        }).catch(reject);
-    });
+    return this.request.get(this.API.questions)
+      .toPromise()
+      .then((data: any) => {
+        this.questions = data.questions.map(q => {
+          const question = {
+            id: q.id,
+            title: q.title,
+            link: q.url,
+            answers: q.answer_count,
+            content: q.summary,
+            timestamp: q.added_at,
+            tags: q.tags,
+            view: q.view_count,
+            vote: q.score,
+          };
+          return question;
+        });
+
+        return this.questions;
+      });
   }
 }
diff --git a/src/providers/fb/fb.ts b/src/providers/fb/fb.ts
deleted file mode 100644
index 4a12aa8..0000000
--- a/src/providers/fb/fb.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Injectable } from '@angular/core';
-import 'rxjs/add/operator/map';
-import ENV from '@environment';
-
-import { Facebook } from 'fb';
-import * as _ from 'lodash';
-
-
-/*
-  Generated class for the Fb provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
-@Injectable()
-export class FB {
-  private fb:Facebook;
-  constructor() {
-    this.fb = new Facebook(ENV.FB_CONFIG);
-  }
-
-  api(urlParts) {
-    return new Promise((resolve, reject) => {
-      this.fb.api(urlParts.join('/'), res => {
-        if (!res || res.error) {
-          return reject(res);
-        } else {
-          return resolve(res);
-        }
-      });
-    });
-  }
-
-  getPagePosts(page) {
-    return new Promise((resolve, reject) => {
-      this.api([page, 'posts']).then((res:any) => {
-        var posts = _.compact(_.map(res.data, p => {
-          var post = {
-            id: p.id,
-            link: 'https://facebook.com/' + p.id,
-            content: p.message,
-            origin: 'facebook',
-          };
-
-          if (_.isEmpty(post.content)) {
-            return null;
-          } else {
-            return post;
-          }
-        }));
-
-        return resolve(posts);
-      }).catch(reject);
-    });
-  }
-}
diff --git a/src/providers/fedo-cal/fedo-cal.ts b/src/providers/fedo-cal/fedo-cal.ts
index 3a6288a..cfcc03d 100644
--- a/src/providers/fedo-cal/fedo-cal.ts
+++ b/src/providers/fedo-cal/fedo-cal.ts
@@ -4,7 +4,7 @@ import 'rxjs/add/operator/map';
 import * as _ from 'lodash';
 import * as moment from 'moment-timezone';
 
-import { Request } from '../request/request';
+import { Http } from '../http/http';
 
 const API_ENDPOINT = 'https://apps.fedoraproject.org/calendar/api/';
 const API = {
@@ -19,8 +19,8 @@ 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) {
+function calendarNameToDisplayName(name: string): string {
+  switch (name) {
     case 'i18n':
       return 'i18n';
     case 'fesco':
@@ -39,10 +39,10 @@ function calendarNameToDisplayName(name:string) :string {
 */
 @Injectable()
 export class FedoCal {
-  private calendars:any;
-  private meetings:any;
+  private calendars: any;
+  private meetings: any;
 
-  constructor(private request:Request) {
+  constructor(private request: Http) {
     this.calendars = [];
     this.meetings = [];
   }
@@ -52,21 +52,20 @@ export class FedoCal {
       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);
-    });
+    return this.request.get(API.calendars)
+      .toPromise()
+      .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 this.calendars
+      });
   }
 
   getMeetings(calendar) {
@@ -74,42 +73,41 @@ export class FedoCal {
       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);
-    });
+    return this.request.get(API.meetings, { calendar: calendar })
+      .toPromise()
+      .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 this.meetings[calendar];
+      });
   }
 }
 
@@ -117,4 +115,4 @@ export class FedoCal {
 function dateToMoment(date, time, timezone) {
   return moment.tz(date + 'T' + time + 'Z', timezone).tz('Etc/UTC');
 }
- 
+
diff --git a/src/providers/fedora-mag/fedora-mag.ts b/src/providers/fedora-mag/fedora-mag.ts
index 196fc80..fa41391 100644
--- a/src/providers/fedora-mag/fedora-mag.ts
+++ b/src/providers/fedora-mag/fedora-mag.ts
@@ -3,13 +3,13 @@ import 'rxjs/add/operator/map';
 
 import * as _ from 'lodash';
 
-import { Request } from '../request/request';
+import { Http } from '../http/http';
 
 const API_ENDPOINT = 'https://fedoramagazine.org/wp-json/wp/v2';
 const API = {
   base: [API_ENDPOINT],
   posts: [API_ENDPOINT, 'posts'],
-//  media_url: [API_ENDPOINT, 'media'],
+  //  media_url: [API_ENDPOINT, 'media'],
 };
 
 /*
@@ -20,9 +20,9 @@ const API = {
 */
 @Injectable()
 export class FedoraMag {
-  private posts:any;
-  private media_url:any;
-  constructor(private request:Request) {
+  private posts: any;
+  private media_url: any;
+  constructor(private request: Http) {
     this.posts = [];
     this.media_url = [];
   }
@@ -32,53 +32,53 @@ export class FedoraMag {
       return Promise.resolve(this.posts);
     }
 
-    return new Promise((resolve, reject) => {
-      this.request.get(API.posts)
-        .then(data => {
-          this.posts = _.map(data, p => {
-            var excerpt = p.excerpt.rendered;
-            var post = {
-              id: p.id,
-              link: p.guid.rendered,
-              title: p.title.rendered,
-              image: p.featured_media,
-              excerpt: _.truncate(
-                _.truncate(excerpt, {
-                  length: excerpt.length,
-                  separator: '<a', omission: ''
-                }), { length: 150, separator: ' ' }
-              ),
-              content: p.content.rendered,
-              date: p.date_gmt,
-            };
-            //image_url = func_url(post.image);
-            return post;
-          });
+    return this.request.get(API.posts)
+      .toPromise()
+      .then(data => {
+        this.posts = _.map(data, p => {
+          var excerpt = p.excerpt.rendered;
+          var post = {
+            id: p.id,
+            link: p.guid.rendered,
+            title: p.title.rendered,
+            image: p.featured_media,
+            excerpt: _.truncate(
+              _.truncate(excerpt, {
+                length: excerpt.length,
+                separator: '<a', omission: ''
+              }), { length: 150, separator: ' ' }
+            ),
+            content: p.content.rendered,
+            date: p.date_gmt,
+          };
+          //image_url = func_url(post.image);
+          return post;
+        });
 
-          return resolve(this.posts);
-        }).catch(reject);
-    });
+        return this.posts;
+      });
   }
-/*  getMedia_url(posts) {
-    if (!_.isEmpty(this.media_url[posts])) {
-      return Promise.resolve(this.media_url[posts]);
-    }
 
-    return new Promise((resolve, reject) => {
-      this.request.get(this.API.media_url,{ posts : posts })
-        .then(data => {
-          this.media_url[posts] = _.map(data, i => {
-          //  var excerpt = p.excerpt.rendered;
-            var img_url = {
-              img_id: i.id,
-              img_link: i.guid.rendered,
-            };
-            //image_url = func_url(post.image);
-            return post;
-          });
+  /*  getMedia_url(posts) {
+      if (!_.isEmpty(this.media_url[posts])) {
+        return Promise.resolve(this.media_url[posts]);
+      }
+
+      return new Promise((resolve, reject) => {
+        this.request.get(this.API.media_url,{ posts : posts })
+          .then(data => {
+            this.media_url[posts] = _.map(data, i => {
+            //  var excerpt = p.excerpt.rendered;
+              var img_url = {
+                img_id: i.id,
+                img_link: i.guid.rendered,
+              };
+              //image_url = func_url(post.image);
+              return post;
+            });
 
-          return resolve(this.posts);
-        }).catch(reject);
-    });
-  }*/
+            return resolve(this.posts);
+          }).catch(reject);
+      });
+    }*/
 }
diff --git a/src/providers/http/http.ts b/src/providers/http/http.ts
new file mode 100644
index 0000000..f3f4402
--- /dev/null
+++ b/src/providers/http/http.ts
@@ -0,0 +1,129 @@
+import 'rxjs/add/operator/finally';
+import 'rxjs/add/operator/map';
+
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { SpinnerDialog } from '@ionic-native/spinner-dialog';
+import { Toast } from '@ionic-native/toast';
+import { IonicConfig } from '../ionic-config/ionic-config';
+
+import * as _ from 'lodash';
+import * as querystring from 'query-string';
+import { Observable } from 'rxjs/Observable';
+
+/*
+  Generated class for the Http provider.
+
+  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
+  for more info on providers and Angular 2 DI.
+*/
+@Injectable()
+export class Http {
+  private proxies:any;
+  private isCordova:boolean;
+  private isLiveReload:boolean;
+
+  constructor(private httpClient:HttpClient, config:IonicConfig,
+    /* private spinnerDialog:SpinnerDialog, */ private toast:Toast) {
+    this.proxies = config.get('proxies');
+
+    this.isCordova = window.hasOwnProperty('cordova');
+    this.isLiveReload = false;
+
+    // this.http
+    //   .get('http://localhost:8100')
+    //   .subscribe(
+    //     res => this.isLiveReload = !_.isEmpty(res),
+    //     err => console.log(this.useProxy())
+    //   );
+  }
+
+  // private startHandler = () => {
+  //   this.spinnerDialog.show();
+  // }
+
+  // private stopHandler = () => {
+  //   this.spinnerDialog.hide();
+  // }
+
+  // private errorHandler = (error, reject) => {
+  //   this.toast.showShortBottom('Something went wrong.');
+  //   this.spinnerDialog.hide();
+  //   return reject(error);
+  // }
+
+  private useProxy() {
+    if (this.isLiveReload || !this.isCordova) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  private buildURL(parts:Array<string>):string {
+    return _.map(
+      parts, part => _.trim(part, '/')
+    ).join('/');
+  }
+
+  private processURL(urlParts) {
+    const url = this.buildURL(urlParts);
+
+    let finalURL = url;
+
+    if (this.useProxy()) {
+      this.proxies.forEach(proxy => {
+        if (url.startsWith(proxy.proxyUrl)) {
+          finalURL = _.replace(
+            url, proxy.proxyUrl, proxy.path
+          );
+          return false;
+        }
+      });
+
+      finalURL = _.trim(finalURL + '/');
+    }
+
+    return finalURL;
+  }
+
+  get<T>(urlParts, query?:any, headers = {}): Observable<T> {
+    let url = this.processURL(urlParts);
+
+    if (!_.isEmpty(query)) {
+      url = _.trimEnd(url, '/');
+      var query = querystring.stringify(query);
+      url += '?' + query;
+    }
+
+    // this.startHandler();
+    // return new Promise((resolve, reject) => {
+      return this.httpClient.get<T>(url, { headers });
+        // .subscribe(
+        //   response => resolve(response),
+        //   error => this.errorHandler.bind(this, error, reject),
+        //   this.stopHandler
+        // );
+    // });
+  }
+
+  post(urlParts, data = {}, headers = {}) {
+    const url = this.processURL(urlParts);
+    const body = JSON.stringify(data);
+
+    _.defaults(headers, {
+      'Content-Type': 'application/json'
+    });
+
+    // this.startHandler();
+    // return new Promise((resolve, reject) => {
+      return this.httpClient.post(url, body, { headers });
+        // .subscribe(
+        //   response => resolve(response),
+        //   error => this.errorHandler.bind(this, error, reject),
+        //   this.stopHandler
+        // );
+    // });
+  }
+}
+
diff --git a/src/providers/request/request.ts b/src/providers/request/request.ts
deleted file mode 100644
index 3a41e5f..0000000
--- a/src/providers/request/request.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import 'rxjs/add/operator/finally';
-import 'rxjs/add/operator/map';
-
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { SpinnerDialog } from '@ionic-native/spinner-dialog';
-import { Toast } from '@ionic-native/toast';
-import { IonicConfig } from '../ionic-config/ionic-config';
-
-import * as _ from 'lodash';
-import * as querystring from 'query-string';
-
-/*
-  Generated class for the Request provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
-@Injectable()
-export class Request {
-  private proxies:any;
-  private isCordova:boolean;
-  private isLiveReload:boolean;
-
-  constructor(private http:HttpClient, config:IonicConfig,
-    private spinnerDialog:SpinnerDialog, private toast:Toast) {
-    this.proxies = config.get('proxies');
-
-    this.isCordova = window.hasOwnProperty('cordova');
-    this.isLiveReload = false;
-
-    // this.http
-    //   .get('http://localhost:8100')
-    //   .subscribe(
-    //     res => this.isLiveReload = !_.isEmpty(res),
-    //     err => console.log(this.useProxy())
-    //   );
-  }
-
-  private startHandler = () => {
-    this.spinnerDialog.show();
-  }
-
-  private stopHandler = () => {
-    this.spinnerDialog.hide();
-  }
-
-  private errorHandler = (error, reject) => {
-    this.toast.showShortBottom('Something went wrong.');
-    this.spinnerDialog.hide();
-    return reject(error);
-  }
-
-  private useProxy() {
-    if (this.isLiveReload || !this.isCordova) {
-      return true;
-    } else {
-      return false;
-    }
-  }
-
-  private buildURL(parts:Array<string>):string {
-    return _.map(
-      parts, part => _.trim(part, '/')
-    ).join('/');
-  }
-
-  private processURL(urlParts) {
-    const url = this.buildURL(urlParts);
-
-    let finalURL = url;
-
-    if (this.useProxy()) {
-      this.proxies.forEach(proxy => {
-        if (url.startsWith(proxy.proxyUrl)) {
-          finalURL = _.replace(
-            url, proxy.proxyUrl, proxy.path
-          );
-          return false;
-        }
-      });
-
-      finalURL = _.trim(finalURL + '/');
-    }
-
-    return finalURL;
-  }
-
-  get(urlParts, query?:any, headers = {}) {
-    let url = this.processURL(urlParts);
-
-    if (!_.isEmpty(query)) {
-      url = _.trimEnd(url, '/');
-      var query = querystring.stringify(query);
-      url += '?' + query;
-    }
-
-    this.startHandler();
-    return new Promise((resolve, reject) => {
-      this.http
-        .get(url, { headers })
-        .subscribe(
-          response => resolve(response),
-          error => this.errorHandler.bind(this, error, reject),
-          this.stopHandler
-        );
-    });
-  }
-
-  post(urlParts, data = {}, headers = {}) {
-    const url = this.processURL(urlParts);
-    const body = JSON.stringify(data);
-
-    _.defaults(headers, {
-      'Content-Type': 'application/json'
-    });
-
-    this.startHandler();
-    return new Promise((resolve, reject) => {
-      this.http
-        .post(url, body, { headers })
-        .subscribe(
-          response => resolve(response),
-          error => this.errorHandler.bind(this, error, reject),
-          this.stopHandler
-        );
-    });
-  }
-}
-
diff --git a/src/providers/social/facebook.ts b/src/providers/social/facebook.ts
new file mode 100644
index 0000000..a5056e9
--- /dev/null
+++ b/src/providers/social/facebook.ts
@@ -0,0 +1,48 @@
+import { Injectable } from '@angular/core';
+import 'rxjs/add/operator/map';
+import ENV from '@environment';
+import { Facebook } from 'fb';
+import { compact } from 'lodash-es';
+import { SocialProvider, Post } from './social';
+
+/*
+  Generated class for the Fb provider.
+
+  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
+  for more info on providers and Angular 2 DI.
+*/
+@Injectable()
+export class FacebookProvider implements SocialProvider {
+  private fb: Facebook;
+  constructor() {
+    this.fb = new Facebook(ENV.FB_CONFIG);
+  }
+
+  private api(uri) {
+    return new Promise((resolve, reject) => {
+      this.fb.api(uri, res => {
+        if (!res || res.error) {
+          return reject(res);
+        } else {
+          return resolve(res);
+        }
+      });
+    });
+  }
+
+  public async getPosts(page:string, args?): Promise<Post[]> {
+    const res:any = await this.api(`${page}/posts`);
+    const posts = compact<Post>(res.data.map(p => {
+          const post = {
+            id: p.id,
+            link: 'https://facebook.com/' + p.id,
+            content: p.message,
+            origin: 'facebook',
+          };
+
+          return post.content ? post : null;
+        }));
+
+        return posts;
+  }
+}
diff --git a/src/providers/social/social.ts b/src/providers/social/social.ts
new file mode 100644
index 0000000..b8d9482
--- /dev/null
+++ b/src/providers/social/social.ts
@@ -0,0 +1,18 @@
+
+export const FACEBOOK = 'facebook';
+export const TWITTER = 'twitter';
+
+export interface Post {
+  id:string,
+  origin:string,
+  link:string,
+  // date: Date,
+}
+
+export interface GetPostExtraArgs {
+  offset?: string,
+}
+
+export interface SocialProvider {
+  getPosts(resID: string, args?:GetPostExtraArgs): Promise<Post[]>
+}
diff --git a/src/providers/social/twitter.ts b/src/providers/social/twitter.ts
new file mode 100644
index 0000000..af1bd6b
--- /dev/null
+++ b/src/providers/social/twitter.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+import 'rxjs/add/operator/map';
+import * as _ from 'lodash';
+
+import ENV from '@environment';
+
+import { Http } from '../http/http';
+import { SocialProvider, Post } from './social';
+
+
+const API_ENDPOINT = 'https://api.twitter.com/1.1/';
+
+const API: any = {
+  base: [API_ENDPOINT],
+  statuses: [API_ENDPOINT, 'statuses'],
+}
+API.timeline = API.statuses.concat(['user_timeline.json']);
+
+
+/*
+  Generated class for the Twitter provider.
+
+  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
+  for more info on providers and Angular 2 DI.
+*/
+@Injectable()
+export class TwitterProvider implements SocialProvider {
+
+  constructor(private http: Http) { }
+
+  public async getPosts(handle:string, args?): Promise<Post[]> {
+    const response = (await this.http.get(
+      API.timeline, { screen_name: handle },
+      { 'Authorization': 'Bearer ' + ENV.TWITTER_CONFIG.BEARER_TOKEN }
+    ).toPromise() as any[]);
+
+    return response.map<Post>(t => {
+      return {
+        id: t.id,
+        link: 'https://twitter.com/statuses/' + t.id_str,
+        content: t.text,
+        origin: 'twitter',
+      };
+    });
+  }
+}
+
diff --git a/src/providers/tw/tw.ts b/src/providers/tw/tw.ts
deleted file mode 100644
index 5cbb6b2..0000000
--- a/src/providers/tw/tw.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Injectable } from '@angular/core';
-import 'rxjs/add/operator/map';
-import ENV from '@environment';
-
-import * as _ from 'lodash';
-import { Request } from '../request/request';
-
-
-const API_ENDPOINT = 'https://api.twitter.com/1.1/';
-
-const API:any = {
-  base: [API_ENDPOINT],
-  statuses: [API_ENDPOINT, 'statuses'],
-}
-API.timeline = API.statuses.concat(['user_timeline.json']);
-
-/*
-  Generated class for the Tw provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
-@Injectable()
-export class Tw {
-
-  constructor(private request:Request) { }
-
-  getTimelineTweets(user) {
-    return new Promise((resolve, reject) => {
-      this.request.get(
-        API.timeline, { screen_name: user },
-        { 'Authorization': 'Bearer ' + ENV.TWITTER_CONFIG.BEARER_TOKEN }
-      ).then(data => {
-          var tweets = _.map(data, t => {
-            return {
-              id: t.id,
-              link: 'https://twitter.com/statuses/' + t.id_str,
-              content: t.text,
-              origin: 'twitter',
-            };
-          });
-          return resolve(tweets);
-      }).catch(reject);
-    });
-  }
-}
-
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/utils.ts

From 6c79e17ce524d0525ccd5bf279608dd5f2fba781 Mon Sep 17 00:00:00 2001
From: Amitosh Swain Mahapatra <amitosh.swain@gmail.com>
Date: May 30 2018 07:00:27 +0000
Subject: [PATCH 2/4] Use Observables instead of Promises to match with Angular API


---

diff --git a/ionic.config.json b/ionic.config.json
index 5b16c8f..12b679c 100644
--- a/ionic.config.json
+++ b/ionic.config.json
@@ -3,23 +3,19 @@
   "app_id": "",
   "proxies": [
     {
-      "path": "/facebook/",
-      "proxyUrl": "https://graph.facebook.com/v2.6/"
-    },
-    {
-      "path": "/twitter/",
+      "path": "/twitter",
       "proxyUrl": "https://api.twitter.com/1.1/"
     },
     {
-      "path": "/fedocal/",
+      "path": "/fedocal",
       "proxyUrl": "https://apps.fedoraproject.org/calendar/api/"
     },
     {
-      "path": "/fedoramag/",
+      "path": "/fedora-magazine",
       "proxyUrl": "https://fedoramagazine.org/wp-json/wp/v2/"
     },
     {
-      "path": "/askfedora/",
+      "path": "/ask-fedora",
       "proxyUrl": "https://ask.fedoraproject.org/en/api/v1/"
     }
   ],
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index df270df..d3a02b7 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,25 +1,25 @@
-import {Component, ViewChild} from '@angular/core';
+import { Component, ViewChild } from '@angular/core';
 import { Platform, NavController } from 'ionic-angular';
 
-import {MagazinePage} from '../pages/magazine/magazine';
-import {AskPage} from '../pages/ask/ask';
-import {CalendarPage} from '../pages/calendar/calendar';
-import {SocialPage} from '../pages/social/social';
-import {WomenPage} from '../pages/women/women';
-import {FirstPage} from '../pages/first/first';
+import { MagazinePage } from '../pages/magazine/magazine';
+import { AskPage } from '../pages/ask/ask';
+import { CalendarPage } from '../pages/calendar/calendar';
+import { SocialPage } from '../pages/social/social';
+import { WomenPage } from '../pages/women/women';
+import { FirstPage } from '../pages/first/first';
 import { SplashScreen } from '@ionic-native/splash-screen';
 
 @Component({
   templateUrl: 'app.html',
 })
 export class MyApp {
-  pages:Array<{title:string, component:any}>;
-  rootPage:any;
+  pages: Array<{ title: string, component: any }>;
+  rootPage: any;
 
-  @ViewChild('content') nav:NavController;
+  @ViewChild('content') nav: NavController;
 
 
-  constructor(platform:Platform, splashScreen:SplashScreen) {
+  constructor(platform: Platform, splashScreen: SplashScreen) {
     // used for an example of ngFor and navigation
     this.pages = [
       { title: 'Home', component: FirstPage },
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 1378f59..3c5e34c 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -18,10 +18,6 @@ import { SocialPage } from '../pages/social/social';
 import { WomenPage } from '../pages/women/women';
 
 import { Browser } from '../providers/browser/browser';
-import { Http } from '../providers/http/http';
-import { IonicConfig } from '../providers/ionic-config/ionic-config';
-import { FacebookProvider } from '../providers/social/facebook';
-import { TwitterProvider } from '../providers/social/twitter';
 
 @NgModule({
   declarations: [
@@ -50,16 +46,12 @@ import { TwitterProvider } from '../providers/social/twitter';
   ],
   providers: [
     Browser,
-    IonicConfig,
     InAppBrowser,
-    Http,
     SocialSharing,
     SplashScreen,
     SpinnerDialog,
     StatusBar,
     Toast,
-    FacebookProvider,
-    TwitterProvider,
     { provide: ErrorHandler, useClass: IonicErrorHandler },
   ]
 })
diff --git a/src/pages/ask/ask.ts b/src/pages/ask/ask.ts
index 7cf52af..e61edc1 100644
--- a/src/pages/ask/ask.ts
+++ b/src/pages/ask/ask.ts
@@ -29,7 +29,7 @@ export class AskPage {
   updateQuestions() {
     this.askFedora
       .getQuestions()
-      .then(questions => {
+      .subscribe(questions => {
         this.questions = questions;
       });
   }
diff --git a/src/pages/calendar/calendar.html b/src/pages/calendar/calendar.html
index 9e5eb6b..b612521 100644
--- a/src/pages/calendar/calendar.html
+++ b/src/pages/calendar/calendar.html
@@ -16,8 +16,8 @@
     <ion-item>
 
       <ion-select [(ngModel)]="selectedCalendar" (ngModelChange)="updateMeetings()">
-        <ion-option *ngFor="let calendar of calendars" [value]="calendar.real_name" [selected]="calendar.real_name == selectedCalendar">
-          {{ calendar.display_name }}
+        <ion-option *ngFor="let calendar of calendars" [value]="calendar.realName" [selected]="calendar.realName == selectedCalendar">
+          {{ calendar.displayName }}
         </ion-option>
       </ion-select>
     </ion-item>
@@ -27,12 +27,12 @@
           <strong>{{ meeting.name }}</strong>
         </ion-card-header>
         <ion-card-content>
-          {{ meeting.display_description }}
+          {{ meeting.description }}
         </ion-card-content>
         <ion-row center>
           <ion-col width-67 padding>
-            <p>{{ meeting.display_date }}</p>
-            <p>{{ meeting.display_time }}</p>
+            <p>{{ meeting.displayTime.dateString }}</p>
+            <p>{{ meeting.displayTime.timeString }}</p>
           </ion-col>
           <ion-col width-33 padding>
             <button ion-button id="add_button" small tappable (click)="addToCalendar(meeting)">
diff --git a/src/pages/calendar/calendar.ts b/src/pages/calendar/calendar.ts
index b5b0771..1ec2f2b 100644
--- a/src/pages/calendar/calendar.ts
+++ b/src/pages/calendar/calendar.ts
@@ -1,7 +1,7 @@
 import { Component } from '@angular/core';
 import { Calendar } from '@ionic-native/calendar';
 
-import { FedoCal } from '../../providers/fedo-cal/fedo-cal';
+import { FedoCalService, Calendar as CalendarType } from '../../providers/fedo-cal/fedo-cal';
 
 /*
   Generated class for the CalendarPage page.
@@ -14,14 +14,14 @@ const DEFAULT_CALENDAR = 'QA';
 
 @Component({
   templateUrl: 'calendar.html',
-  providers: [ FedoCal, Calendar ]
+  providers: [FedoCalService, Calendar]
 })
 export class CalendarPage {
-  private calendars:Array<any>;
-  private meetings:Array<any>;
-  private selectedCalendar:string;
+  private calendars: CalendarType[];
+  private meetings: Array<any>;
+  private selectedCalendar: string;
 
-  constructor(private fedoCal:FedoCal, private calendar:Calendar) {
+  constructor(private fedoCal: FedoCalService, private calendar: Calendar) {
     this.calendars = [];
     this.meetings = [];
 
@@ -36,7 +36,7 @@ export class CalendarPage {
   updateCalendars() {
     this.fedoCal
       .getCalendars()
-      .then(calendars => {
+      .subscribe(calendars => {
         this.calendars = calendars;
       });
   }
@@ -44,7 +44,7 @@ export class CalendarPage {
   updateMeetings() {
     this.fedoCal
       .getMeetings(this.selectedCalendar)
-      .then(meetings => {
+      .subscribe(meetings => {
         this.meetings = meetings;
       });
   }
diff --git a/src/pages/first/first.ts b/src/pages/first/first.ts
index 5bce2e7..e6574ae 100644
--- a/src/pages/first/first.ts
+++ b/src/pages/first/first.ts
@@ -3,7 +3,6 @@ import { NavController } from 'ionic-angular';
 import { SocialSharing } from '@ionic-native/social-sharing';
 
 import { Browser } from '../../providers/browser/browser';
-import { FedoraMag } from '../../providers/fedora-mag/fedora-mag';
 import { MagazinePage } from '../magazine/magazine';
 import { AskPage } from '../ask/ask';
 import { CalendarPage } from '../calendar/calendar';
@@ -17,27 +16,15 @@ import { SocialPage } from '../social/social';
 */
 @Component({
   templateUrl: 'first.html',
-  providers: [ FedoraMag ]
 })
 
 export class FirstPage {
   private posts: Array<any>;
 
-  constructor(private nav: NavController, private browser: Browser,
-    private fedoraMag: FedoraMag, private socialSharing: SocialSharing) {
+  constructor(private nav: NavController, private browser: Browser, private socialSharing: SocialSharing) {
     this.posts = [];
   }
 
-  ngOnInit() {
-    this.updatePosts();
-  }
-
-  updatePosts() {
-    this.fedoraMag.getPosts().then(posts => {
-      this.posts = posts;
-    });
-  }
-
   openPost(event) {
     this.browser.open(event.link);
   }
@@ -58,11 +45,4 @@ export class FirstPage {
   login(event) {
 
   }
-
-  sharePost(event) {
-    this.socialSharing.share(
-      event.title, event.title,
-      null, event.link
-    );
-  }
 }
diff --git a/src/pages/magazine/magazine.ts b/src/pages/magazine/magazine.ts
index 7f2f426..b0c724f 100644
--- a/src/pages/magazine/magazine.ts
+++ b/src/pages/magazine/magazine.ts
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
 import { SocialSharing } from '@ionic-native/social-sharing';
 
 import { Browser } from '../../providers/browser/browser';
-import { FedoraMag } from '../../providers/fedora-mag/fedora-mag';
+import { FedoraMagazineService, Post } from '../../providers/fedora-magazine/fedora-magazine';
 
 /*
   Generated class for the MagazinePage page.
@@ -12,13 +12,13 @@ import { FedoraMag } from '../../providers/fedora-mag/fedora-mag';
 */
 @Component({
   templateUrl: 'magazine.html',
-  providers: [FedoraMag],
+  providers: [FedoraMagazineService],
 })
 export class MagazinePage {
-  private posts:Array<any>;
+  private posts: Post[];
 
-  constructor(private browser:Browser,
-    private fedoraMag:FedoraMag, private socialSharing:SocialSharing) {
+  constructor(private browser: Browser,
+    private fedoraMag: FedoraMagazineService, private socialSharing: SocialSharing) {
     this.posts = [];
   }
 
@@ -27,9 +27,10 @@ export class MagazinePage {
   }
 
   updatePosts() {
-    this.fedoraMag.getPosts().then(posts => {
-      this.posts = posts;
-    });
+    this.fedoraMag.getPosts()
+      .subscribe(posts => {
+        this.posts = posts;
+      });
   }
 
   openPost(event) {
diff --git a/src/pages/social/social.html b/src/pages/social/social.html
index 835a9e5..736e9b3 100644
--- a/src/pages/social/social.html
+++ b/src/pages/social/social.html
@@ -14,23 +14,23 @@
 
   <img id="social_logo" src="assets/img/social_icon.svg">
 
-  <ion-list *ngIf="updates.length !== 0" id="social_card">
-    <ion-card *ngFor="let update of updates" >
+  <ion-list *ngIf="posts.length !== 0" id="social_card">
+    <ion-card *ngFor="let post of posts" >
 
         <ion-row>
-        <ion-col tappable (click)="openUpdate(update)">
-          {{ update.content }}
+        <ion-col tappable (click)="openPost(post)">
+          {{ post.content }}
         </ion-col>
 
       </ion-row>
       <ion-row >
         <ion-col center >
           <button ion-button small clear disabled class="s-icon">
-            <ion-icon name="logo-{{ update.origin }}"></ion-icon>
+            <ion-icon name="logo-{{ post.origin }}"></ion-icon>
           </button>
         </ion-col>
         <ion-col center>
-          <button ion-button small clear tappable color="dark" (click)="shareUpdate(update)">
+          <button ion-button small clear tappable color="dark" (click)="sharePost(post)">
             <ion-icon name="share"></ion-icon>
           </button>
         </ion-col>
diff --git a/src/pages/social/social.ts b/src/pages/social/social.ts
index 0b30a6f..185dd8d 100644
--- a/src/pages/social/social.ts
+++ b/src/pages/social/social.ts
@@ -5,6 +5,12 @@ import { Browser } from '../../providers/browser/browser';
 import { FacebookProvider } from '../../providers/social/facebook';
 import { TwitterProvider } from '../../providers/social/twitter';
 import { Post } from '../../providers/social/social';
+import { forkJoin } from 'rxjs/observable/forkJoin';
+
+const HANDLE = {
+  FB: 'fedoraqa',
+  TWITTER: 'fedora_qa',
+};
 
 /*
   Generated class for the SocialPage page.
@@ -18,53 +24,28 @@ import { Post } from '../../providers/social/social';
 })
 export class SocialPage {
 
-  private posts:Post[];
-  private tweets:Post[];
-  private updates:Array<any>;
-  private USER:any;
-
-  constructor(private browser:Browser, private fb:FacebookProvider, private tw:TwitterProvider,
-              private socialSharing:SocialSharing) {
+  private posts: Post[];
+  constructor(private browser: Browser, private fb: FacebookProvider, private twitter: TwitterProvider,
+    private socialSharing: SocialSharing) {
     this.posts = [];
-    this.tweets = [];
-    this.updates = [];
-
-    this.USER = {
-      FB: 'fedoraqa',
-      TW: 'fedora_qa',
-    };
   }
 
   ngOnInit() {
-    this.updateUpdates();
+    this.updatePosts();
   }
 
-  updateUpdates() {
-    this.fb
-      .getPosts(this.USER.FB)
-      .then((posts:Array<any>) => {
-        this.posts = posts;
-        this.mergeUpdates();
+  private updatePosts() {
+    forkJoin(this.fb.getPosts(HANDLE.FB), this.twitter.getPosts(HANDLE.TWITTER))
+      .subscribe(values => {
+        this.posts = [...values[0], ...values[1]] as Post[];
       });
-
-    this.tw
-      .getPosts(this.USER.TW)
-      .then((tweets:Array<any>) => {
-        this.tweets = tweets;
-        this.mergeUpdates();
-      });
-  }
-
-  mergeUpdates() {
-    // TODO: Merge as per ascending order of timestamps?
-    this.updates = [ ...this.posts, ...this.tweets ];
   }
 
-  openUpdate(event) {
+  openPost(event) {
     this.browser.open(event.link);
   }
 
-  shareUpdate(event) {
+  sharePost(event) {
     this.socialSharing.share(
       event.title,
       event.title,
diff --git a/src/pages/women/women.ts b/src/pages/women/women.ts
index 31282e2..7ef83df 100644
--- a/src/pages/women/women.ts
+++ b/src/pages/women/women.ts
@@ -1,13 +1,4 @@
 import { Component } from '@angular/core';
-import { NavController } from 'ionic-angular';
-import { SocialSharing } from '@ionic-native/social-sharing';
-
-import { Browser } from '../../providers/browser/browser';
-import { FedoraMag } from '../../providers/fedora-mag/fedora-mag';
-import { MagazinePage } from '../magazine/magazine';
-import { AskPage } from '../ask/ask';
-import { CalendarPage } from '../calendar/calendar';
-import { SocialPage } from '../social/social';
 
 /*
   Generated class for the MagazinePage page.
@@ -17,51 +8,6 @@ import { SocialPage } from '../social/social';
 */
 @Component({
   templateUrl: 'women.html',
-  providers: [Browser, FedoraMag],
 })
 export class WomenPage {
-  posts: Array<any>;
-
-  constructor(private nav: NavController, private browser: Browser,
-    private fedoraMag: FedoraMag, private socialSharing: SocialSharing) {
-    this.posts = [];
-  }
-
-  ngOnInit() {
-    this.updatePosts();
-  }
-
-  updatePosts() {
-    this.fedoraMag.getPosts().then(posts => {
-      this.posts = posts;
-    });
-  }
-
-  openPost(event) {
-    this.browser.open(event.link);
-  }
-
-  openMag() {
-    this.nav.push(MagazinePage);
-  }
-  openAsk() {
-    this.nav.push(AskPage);
-  }
-  openSocial() {
-    this.nav.push(SocialPage);
-  }
-  openCal() {
-    this.nav.push(CalendarPage);
-  }
-
-  login(event) {
-
-  }
-
-  sharePost(event) {
-    this.socialSharing.share(
-      event.title, event.title,
-      null, event.link
-    );
-  }
 }
diff --git a/src/providers/ask-fedora/ask-fedora.ts b/src/providers/ask-fedora/ask-fedora.ts
index f32e1cf..9775aff 100644
--- a/src/providers/ask-fedora/ask-fedora.ts
+++ b/src/providers/ask-fedora/ask-fedora.ts
@@ -1,9 +1,12 @@
-import { Injectable } from '@angular/core';
 import 'rxjs/add/operator/map';
+import { Injectable } from '@angular/core';
 
-import { Http } from '../http/http';
+import { HttpClient } from '@angular/common/http';
+import { Platform } from 'ionic-angular';
 
-const API_ENDPOINT = 'https://ask.fedoraproject.org/en/api/v1';
+import { chooseEndpoint } from '../../utils';
+
+const ENDPOINT = chooseEndpoint('/ask-fedora', 'https://ask.fedoraproject.org/en/api/v1');
 
 export interface Question {
   id: string,
@@ -26,42 +29,22 @@ export interface Question {
 */
 @Injectable()
 export class AskFedoraService {
-  private API: any;
-  private questions: Question[];
-
-  constructor(private request: Http) {
-    this.API = {
-      base: [API_ENDPOINT],
-      questions: [API_ENDPOINT, 'questions'],
-    };
-
-    this.questions = [];
+  private endpoint:string;
+  constructor(private http: HttpClient, private platform: Platform) {
   }
 
   getQuestions() {
-    if (this.questions.length) {
-      return Promise.resolve(this.questions);
-    }
-
-    return this.request.get(this.API.questions)
-      .toPromise()
-      .then((data: any) => {
-        this.questions = data.questions.map(q => {
-          const question = {
-            id: q.id,
-            title: q.title,
-            link: q.url,
-            answers: q.answer_count,
-            content: q.summary,
-            timestamp: q.added_at,
-            tags: q.tags,
-            view: q.view_count,
-            vote: q.score,
-          };
-          return question;
-        });
-
-        return this.questions;
-      });
+    return this.http.get(`${ENDPOINT}/questions/`)
+      .map((data: any) => data.questions.map(q => ({
+        id: q.id,
+        title: q.title,
+        link: q.url,
+        answers: q.answer_count,
+        content: q.summary,
+        timestamp: q.added_at,
+        tags: q.tags,
+        view: q.view_count,
+        vote: q.score,
+      })));
   }
 }
diff --git a/src/providers/fedo-cal/fedo-cal.ts b/src/providers/fedo-cal/fedo-cal.ts
index cfcc03d..bb24f93 100644
--- a/src/providers/fedo-cal/fedo-cal.ts
+++ b/src/providers/fedo-cal/fedo-cal.ts
@@ -4,17 +4,16 @@ import 'rxjs/add/operator/map';
 import * as _ from 'lodash';
 import * as moment from 'moment-timezone';
 
-import { Http } from '../http/http';
+import { HttpClient } from '@angular/common/http';
+import { Platform } from 'ionic-angular';
+import { Observable } from 'rxjs/Observable';
+import { chooseEndpoint } from '../../utils';
 
-const API_ENDPOINT = 'https://apps.fedoraproject.org/calendar/api/';
-const API = {
-  base: [API_ENDPOINT],
-  calendars: [API_ENDPOINT, 'calendars'],
-  meetings: [API_ENDPOINT, 'meetings'],
-};
+
+const ENDPOINT = chooseEndpoint('/fedocal', 'https://apps.fedoraproject.org/calendar/api');
 
 const DATE_FORMAT = 'dddd, MMMM Do YYYY';
-const TIME_FORMAT = 'h:mm a z';
+const TIME_FORMAT = 'h:mm A z';
 
 /*
   Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n
@@ -28,7 +27,32 @@ function calendarNameToDisplayName(name: string): string {
     default:
       return /^[A-Z][^\d-]*.*$/.test(name) ? name : _.startCase(name);
   }
+}
+
+export interface Calendar {
+  realName: string,
+  displayName: string,
+  description: string,
+  adminGroup: string,
+  editorGroup: string,
+  contact: string,
+  enabled: boolean,
+}
 
+export interface Meeting {
+  name: string,
+  description: string,
+  location: string,
+  time: Date,
+  timeEnd: Date,
+  displayTime: {
+    dateString: string,
+    timeString: string,
+  },
+  displayTimeEnd: {
+    dateString: string,
+    timeString: string,
+  }
 }
 
 /*
@@ -38,81 +62,54 @@ function calendarNameToDisplayName(name: string): string {
   for more info on providers and Angular 2 DI.
 */
 @Injectable()
-export class FedoCal {
-  private calendars: any;
-  private meetings: any;
-
-  constructor(private request: Http) {
-    this.calendars = [];
-    this.meetings = [];
+export class FedoCalService {
+  private endpoint: string;
+  constructor(private http: HttpClient, private platform: Platform) {
   }
 
-  getCalendars() {
-    if (!_.isEmpty(this.calendars)) {
-      return Promise.resolve(this.calendars);
-    }
-
-    return this.request.get(API.calendars)
-      .toPromise()
-      .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 this.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 this.request.get(API.meetings, { calendar: calendar })
-      .toPromise()
-      .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 this.meetings[calendar];
-      });
+  getMeetings(calendar): 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: {
+            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');
+  const m = moment.tz(`${date} ${time}`, timezone).tz('UTC');
+  return m;
 }
 
diff --git a/src/providers/fedora-mag/fedora-mag.ts b/src/providers/fedora-mag/fedora-mag.ts
deleted file mode 100644
index fa41391..0000000
--- a/src/providers/fedora-mag/fedora-mag.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Injectable } from '@angular/core';
-import 'rxjs/add/operator/map';
-
-import * as _ from 'lodash';
-
-import { Http } from '../http/http';
-
-const API_ENDPOINT = 'https://fedoramagazine.org/wp-json/wp/v2';
-const API = {
-  base: [API_ENDPOINT],
-  posts: [API_ENDPOINT, 'posts'],
-  //  media_url: [API_ENDPOINT, 'media'],
-};
-
-/*
-  Generated class for the FedoraMag provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
-@Injectable()
-export class FedoraMag {
-  private posts: any;
-  private media_url: any;
-  constructor(private request: Http) {
-    this.posts = [];
-    this.media_url = [];
-  }
-
-  getPosts() {
-    if (!_.isEmpty(this.posts)) {
-      return Promise.resolve(this.posts);
-    }
-
-    return this.request.get(API.posts)
-      .toPromise()
-      .then(data => {
-        this.posts = _.map(data, p => {
-          var excerpt = p.excerpt.rendered;
-          var post = {
-            id: p.id,
-            link: p.guid.rendered,
-            title: p.title.rendered,
-            image: p.featured_media,
-            excerpt: _.truncate(
-              _.truncate(excerpt, {
-                length: excerpt.length,
-                separator: '<a', omission: ''
-              }), { length: 150, separator: ' ' }
-            ),
-            content: p.content.rendered,
-            date: p.date_gmt,
-          };
-          //image_url = func_url(post.image);
-          return post;
-        });
-
-        return this.posts;
-      });
-  }
-
-  /*  getMedia_url(posts) {
-      if (!_.isEmpty(this.media_url[posts])) {
-        return Promise.resolve(this.media_url[posts]);
-      }
-
-      return new Promise((resolve, reject) => {
-        this.request.get(this.API.media_url,{ posts : posts })
-          .then(data => {
-            this.media_url[posts] = _.map(data, i => {
-            //  var excerpt = p.excerpt.rendered;
-              var img_url = {
-                img_id: i.id,
-                img_link: i.guid.rendered,
-              };
-              //image_url = func_url(post.image);
-              return post;
-            });
-
-            return resolve(this.posts);
-          }).catch(reject);
-      });
-    }*/
-}
diff --git a/src/providers/fedora-magazine/fedora-magazine.ts b/src/providers/fedora-magazine/fedora-magazine.ts
new file mode 100644
index 0000000..000b79b
--- /dev/null
+++ b/src/providers/fedora-magazine/fedora-magazine.ts
@@ -0,0 +1,68 @@
+import { Injectable } from '@angular/core';
+import 'rxjs/add/operator/map';
+
+import * as _ from 'lodash';
+
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+import { chooseEndpoint } from '../../utils';
+
+const ENDPOINT = chooseEndpoint('/fedora-magazine', 'https://fedoramagazine.org/wp-json/wp/v2');
+
+export interface Post {
+  id: number,
+  link: string,
+  title: string,
+  image: any,
+  excerpt: string,
+  content: string,
+  date: Date
+}
+
+/*
+  Generated class for the FedoraMag provider.
+
+  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
+  for more info on providers and Angular 2 DI.
+*/
+@Injectable()
+export class FedoraMagazineService {
+  constructor(private http: HttpClient) {
+  }
+
+  getPosts(): Observable<Post[]> {
+    return this.http.get(`${ENDPOINT}/posts`)
+      .map((data: any[]) => data.map((post: any) => ({
+        id: post.id,
+        link: post.link,
+        title: post.title.rendered,
+        image: post.featured_media,
+        excerpt: post.excerpt.rendered,
+        content: post.content.rendered,
+        date: new Date(post.date_gmt+'Z'),
+      })));
+  }
+}
+
+  /*  getMedia_url(posts) {
+      if (!_.isEmpty(this.media_url[posts])) {
+        return Promise.resolve(this.media_url[posts]);
+      }
+
+      return new Promise((resolve, reject) => {
+        this.request.get(this.API.media_url,{ posts : posts })
+          .then(data => {
+            this.media_url[posts] = _.map(data, i => {
+            //  var excerpt = p.excerpt.rendered;
+              var img_url = {
+                img_id: i.id,
+                img_link: i.guid.rendered,
+              };
+              //image_url = func_url(post.image);
+              return post;
+            });
+
+            return resolve(this.posts);
+          }).catch(reject);
+      });
+    }*/
diff --git a/src/providers/http/http.ts b/src/providers/http/http.ts
deleted file mode 100644
index f3f4402..0000000
--- a/src/providers/http/http.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import 'rxjs/add/operator/finally';
-import 'rxjs/add/operator/map';
-
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { SpinnerDialog } from '@ionic-native/spinner-dialog';
-import { Toast } from '@ionic-native/toast';
-import { IonicConfig } from '../ionic-config/ionic-config';
-
-import * as _ from 'lodash';
-import * as querystring from 'query-string';
-import { Observable } from 'rxjs/Observable';
-
-/*
-  Generated class for the Http provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
-@Injectable()
-export class Http {
-  private proxies:any;
-  private isCordova:boolean;
-  private isLiveReload:boolean;
-
-  constructor(private httpClient:HttpClient, config:IonicConfig,
-    /* private spinnerDialog:SpinnerDialog, */ private toast:Toast) {
-    this.proxies = config.get('proxies');
-
-    this.isCordova = window.hasOwnProperty('cordova');
-    this.isLiveReload = false;
-
-    // this.http
-    //   .get('http://localhost:8100')
-    //   .subscribe(
-    //     res => this.isLiveReload = !_.isEmpty(res),
-    //     err => console.log(this.useProxy())
-    //   );
-  }
-
-  // private startHandler = () => {
-  //   this.spinnerDialog.show();
-  // }
-
-  // private stopHandler = () => {
-  //   this.spinnerDialog.hide();
-  // }
-
-  // private errorHandler = (error, reject) => {
-  //   this.toast.showShortBottom('Something went wrong.');
-  //   this.spinnerDialog.hide();
-  //   return reject(error);
-  // }
-
-  private useProxy() {
-    if (this.isLiveReload || !this.isCordova) {
-      return true;
-    } else {
-      return false;
-    }
-  }
-
-  private buildURL(parts:Array<string>):string {
-    return _.map(
-      parts, part => _.trim(part, '/')
-    ).join('/');
-  }
-
-  private processURL(urlParts) {
-    const url = this.buildURL(urlParts);
-
-    let finalURL = url;
-
-    if (this.useProxy()) {
-      this.proxies.forEach(proxy => {
-        if (url.startsWith(proxy.proxyUrl)) {
-          finalURL = _.replace(
-            url, proxy.proxyUrl, proxy.path
-          );
-          return false;
-        }
-      });
-
-      finalURL = _.trim(finalURL + '/');
-    }
-
-    return finalURL;
-  }
-
-  get<T>(urlParts, query?:any, headers = {}): Observable<T> {
-    let url = this.processURL(urlParts);
-
-    if (!_.isEmpty(query)) {
-      url = _.trimEnd(url, '/');
-      var query = querystring.stringify(query);
-      url += '?' + query;
-    }
-
-    // this.startHandler();
-    // return new Promise((resolve, reject) => {
-      return this.httpClient.get<T>(url, { headers });
-        // .subscribe(
-        //   response => resolve(response),
-        //   error => this.errorHandler.bind(this, error, reject),
-        //   this.stopHandler
-        // );
-    // });
-  }
-
-  post(urlParts, data = {}, headers = {}) {
-    const url = this.processURL(urlParts);
-    const body = JSON.stringify(data);
-
-    _.defaults(headers, {
-      'Content-Type': 'application/json'
-    });
-
-    // this.startHandler();
-    // return new Promise((resolve, reject) => {
-      return this.httpClient.post(url, body, { headers });
-        // .subscribe(
-        //   response => resolve(response),
-        //   error => this.errorHandler.bind(this, error, reject),
-        //   this.stopHandler
-        // );
-    // });
-  }
-}
-
diff --git a/src/providers/ionic-config/ionic-config.ts b/src/providers/ionic-config/ionic-config.ts
deleted file mode 100644
index a6a4e98..0000000
--- a/src/providers/ionic-config/ionic-config.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Injectable } from '@angular/core';
-
-// this no longer works
-// import * as CONFIG from '../../../ionic.config.json';
-// WORKAROUND: manually set proxy config here
-
-const CONFIG = {
-  proxies: [
-    {
-      path: '/facebook/',
-      proxyUrl: 'https://graph.facebook.com/v2.6/'
-    },
-    {
-      path: '/twitter/',
-      proxyUrl: 'https://api.twitter.com/1.1/'
-    },
-    {
-      path: '/fedocal/',
-      proxyUrl: 'https://apps.fedoraproject.org/calendar/api/'
-    },
-    {
-      path: '/fedoramag/',
-      proxyUrl: 'https://fedoramagazine.org/wp-json/wp/v2/'
-    },
-    {
-      path: '/askfedora/',
-      proxyUrl: 'https://ask.fedoraproject.org/en/api/v1/'
-    }
-  ],
-};
-
-@Injectable()
-export class IonicConfig {
-  private config:any;
-  constructor() {
-    this.config = CONFIG;
-  }
-
-  get(key) {
-    return this.config[key];
-  }
-}
diff --git a/src/providers/social/facebook.ts b/src/providers/social/facebook.ts
index a5056e9..48e4ea0 100644
--- a/src/providers/social/facebook.ts
+++ b/src/providers/social/facebook.ts
@@ -3,7 +3,9 @@ import 'rxjs/add/operator/map';
 import ENV from '@environment';
 import { Facebook } from 'fb';
 import { compact } from 'lodash-es';
-import { SocialProvider, Post } from './social';
+import { SocialProvider, Post, FACEBOOK } from './social';
+import { Observable } from 'rxjs/Observable';
+import { Observer } from 'rxjs/Observer';
 
 /*
   Generated class for the Fb provider.
@@ -19,30 +21,33 @@ export class FacebookProvider implements SocialProvider {
   }
 
   private api(uri) {
-    return new Promise((resolve, reject) => {
+    return Observable.create((emitter: Observer<any>) => {
       this.fb.api(uri, res => {
         if (!res || res.error) {
-          return reject(res);
+          emitter.error(res.error);
         } else {
-          return resolve(res);
+          emitter.next(res);
         }
+        emitter.complete();
       });
     });
   }
 
-  public async getPosts(page:string, args?): Promise<Post[]> {
-    const res:any = await this.api(`${page}/posts`);
-    const posts = compact<Post>(res.data.map(p => {
+  public getPosts(page: string, args?): Observable<Post[]> {
+    return this.api(`${page}/posts`)
+      .map(res => {
+        const posts = compact<Post>(res.data.map(p => {
           const post = {
             id: p.id,
             link: 'https://facebook.com/' + p.id,
             content: p.message,
-            origin: 'facebook',
+            origin: FACEBOOK,
           };
 
           return post.content ? post : null;
         }));
 
         return posts;
+      });
   }
 }
diff --git a/src/providers/social/social.ts b/src/providers/social/social.ts
index b8d9482..8832ffe 100644
--- a/src/providers/social/social.ts
+++ b/src/providers/social/social.ts
@@ -1,3 +1,4 @@
+import { Observable } from "rxjs/Observable";
 
 export const FACEBOOK = 'facebook';
 export const TWITTER = 'twitter';
@@ -6,7 +7,7 @@ export interface Post {
   id:string,
   origin:string,
   link:string,
-  // date: Date,
+  content:string,
 }
 
 export interface GetPostExtraArgs {
@@ -14,5 +15,5 @@ export interface GetPostExtraArgs {
 }
 
 export interface SocialProvider {
-  getPosts(resID: string, args?:GetPostExtraArgs): Promise<Post[]>
+  getPosts(resID: string, args?:GetPostExtraArgs): Observable<Post[]>
 }
diff --git a/src/providers/social/twitter.ts b/src/providers/social/twitter.ts
index af1bd6b..59bf6d2 100644
--- a/src/providers/social/twitter.ts
+++ b/src/providers/social/twitter.ts
@@ -1,21 +1,18 @@
 import { Injectable } from '@angular/core';
 import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/do';
 import * as _ from 'lodash';
 
 import ENV from '@environment';
 
-import { Http } from '../http/http';
 import { SocialProvider, Post } from './social';
+import { HttpClient } from '@angular/common/http';
+import { Platform } from 'ionic-angular';
+import { chooseEndpoint } from '../../utils';
+import { Observable } from 'rxjs/Observable';
+import { from } from 'rxjs/observable/from';
 
-
-const API_ENDPOINT = 'https://api.twitter.com/1.1/';
-
-const API: any = {
-  base: [API_ENDPOINT],
-  statuses: [API_ENDPOINT, 'statuses'],
-}
-API.timeline = API.statuses.concat(['user_timeline.json']);
-
+const ENDPOINT = chooseEndpoint('/twitter', 'https://api.twitter.com/1.1');
 
 /*
   Generated class for the Twitter provider.
@@ -26,22 +23,20 @@ API.timeline = API.statuses.concat(['user_timeline.json']);
 @Injectable()
 export class TwitterProvider implements SocialProvider {
 
-  constructor(private http: Http) { }
-
-  public async getPosts(handle:string, args?): Promise<Post[]> {
-    const response = (await this.http.get(
-      API.timeline, { screen_name: handle },
-      { 'Authorization': 'Bearer ' + ENV.TWITTER_CONFIG.BEARER_TOKEN }
-    ).toPromise() as any[]);
-
-    return response.map<Post>(t => {
-      return {
-        id: t.id,
-        link: 'https://twitter.com/statuses/' + t.id_str,
-        content: t.text,
-        origin: 'twitter',
-      };
-    });
+  private endpoint: string;
+  constructor(private http: HttpClient, private platform: Platform) {
+  }
+
+  public getPosts(handle: string, args?): Observable<Post[]> {
+    return this.http.get(`${ENDPOINT}/statuses/user_timeline.json`, {
+      params: { screen_name: handle },
+      headers: { 'Authorization': 'Bearer ' + ENV.TWITTER_CONFIG.BEARER_TOKEN }
+    }).map((tweetsResponse: any) => tweetsResponse.map(t => ({
+      id: t.id,
+      link: 'https://twitter.com/statuses/' + t.id_str,
+      content: t.text,
+      origin: 'twitter',
+    })));
   }
 }
 
diff --git a/src/utils.ts b/src/utils.ts
index e69de29..25bc712 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -0,0 +1,10 @@
+
+/**
+ * Choose appropriate API end point or the proxy to avoid CORS in Ionic dev server
+ * @param devServerEndpoint endpoint in Ionic dev server
+ * @param appEndpoint endpoint to use in real application
+ */
+export function chooseEndpoint(devServerEndpoint:string, appEndpoint:string) {
+  // courtesy: https://forum.ionicframework.com/t/check-if-run-on-emulator-dev-production-or-livereload/71845/9
+  return window.hasOwnProperty('IonicDevServer') ? devServerEndpoint : appEndpoint;
+}

From 8d98fbdc40b6f058bda6e4152ba168f2d92e007a Mon Sep 17 00:00:00 2001
From: Amitosh Swain Mahapatra <amitosh.swain@gmail.com>
Date: May 30 2018 07:00:27 +0000
Subject: [PATCH 3/4] Fix TS lint errors


---

diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index d3a02b7..a115f57 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -12,8 +12,8 @@ import { SplashScreen } from '@ionic-native/splash-screen';
 @Component({
   templateUrl: 'app.html',
 })
-export class MyApp {
-  pages: Array<{ title: string, component: any }>;
+export class App {
+  pages: { title: string, component: any }[];
   rootPage: any;
 
   @ViewChild('content') nav: NavController;
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 3c5e34c..84f0ade 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -9,7 +9,7 @@ import { SocialSharing } from '@ionic-native/social-sharing';
 import { SpinnerDialog } from '@ionic-native/spinner-dialog';
 import { Toast } from '@ionic-native/toast';
 
-import { MyApp } from './app.component';
+import { App } from './app.component';
 import { FirstPage } from '../pages/first/first';
 import { MagazinePage } from '../pages/magazine/magazine';
 import { AskPage } from '../pages/ask/ask';
@@ -21,7 +21,7 @@ import { Browser } from '../providers/browser/browser';
 
 @NgModule({
   declarations: [
-    MyApp,
+    App,
     FirstPage,
     MagazinePage,
     AskPage,
@@ -32,11 +32,11 @@ import { Browser } from '../providers/browser/browser';
   imports: [
     BrowserModule,
     HttpClientModule,
-    IonicModule.forRoot(MyApp)
+    IonicModule.forRoot(App)
   ],
   bootstrap: [IonicApp],
   entryComponents: [
-    MyApp,
+    App,
     FirstPage,
     MagazinePage,
     AskPage,
diff --git a/src/pages/first/first.ts b/src/pages/first/first.ts
index e6574ae..ed1f25e 100644
--- a/src/pages/first/first.ts
+++ b/src/pages/first/first.ts
@@ -1,7 +1,5 @@
 import { Component } from '@angular/core';
 import { NavController } from 'ionic-angular';
-import { SocialSharing } from '@ionic-native/social-sharing';
-
 import { Browser } from '../../providers/browser/browser';
 import { MagazinePage } from '../magazine/magazine';
 import { AskPage } from '../ask/ask';
@@ -21,7 +19,7 @@ import { SocialPage } from '../social/social';
 export class FirstPage {
   private posts: Array<any>;
 
-  constructor(private nav: NavController, private browser: Browser, private socialSharing: SocialSharing) {
+  constructor(private nav: NavController, private browser: Browser) {
     this.posts = [];
   }
 
diff --git a/src/providers/ask-fedora/ask-fedora.ts b/src/providers/ask-fedora/ask-fedora.ts
index 9775aff..09afe00 100644
--- a/src/providers/ask-fedora/ask-fedora.ts
+++ b/src/providers/ask-fedora/ask-fedora.ts
@@ -1,9 +1,6 @@
 import 'rxjs/add/operator/map';
 import { Injectable } from '@angular/core';
-
 import { HttpClient } from '@angular/common/http';
-import { Platform } from 'ionic-angular';
-
 import { chooseEndpoint } from '../../utils';
 
 const ENDPOINT = chooseEndpoint('/ask-fedora', 'https://ask.fedoraproject.org/en/api/v1');
@@ -29,8 +26,7 @@ export interface Question {
 */
 @Injectable()
 export class AskFedoraService {
-  private endpoint:string;
-  constructor(private http: HttpClient, private platform: Platform) {
+  constructor(private http: HttpClient) {
   }
 
   getQuestions() {
diff --git a/src/providers/fedo-cal/fedo-cal.ts b/src/providers/fedo-cal/fedo-cal.ts
index bb24f93..ce0c7bb 100644
--- a/src/providers/fedo-cal/fedo-cal.ts
+++ b/src/providers/fedo-cal/fedo-cal.ts
@@ -5,7 +5,6 @@ import * as _ from 'lodash';
 import * as moment from 'moment-timezone';
 
 import { HttpClient } from '@angular/common/http';
-import { Platform } from 'ionic-angular';
 import { Observable } from 'rxjs/Observable';
 import { chooseEndpoint } from '../../utils';
 
@@ -63,8 +62,7 @@ export interface Meeting {
 */
 @Injectable()
 export class FedoCalService {
-  private endpoint: string;
-  constructor(private http: HttpClient, private platform: Platform) {
+  constructor(private http: HttpClient) {
   }
 
   getCalendars(): Observable<Calendar[]> {
diff --git a/src/providers/fedora-magazine/fedora-magazine.ts b/src/providers/fedora-magazine/fedora-magazine.ts
index 000b79b..df22c3b 100644
--- a/src/providers/fedora-magazine/fedora-magazine.ts
+++ b/src/providers/fedora-magazine/fedora-magazine.ts
@@ -1,8 +1,5 @@
-import { Injectable } from '@angular/core';
 import 'rxjs/add/operator/map';
-
-import * as _ from 'lodash';
-
+import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs/Observable';
 import { chooseEndpoint } from '../../utils';
diff --git a/src/providers/social/twitter.ts b/src/providers/social/twitter.ts
index 59bf6d2..490c566 100644
--- a/src/providers/social/twitter.ts
+++ b/src/providers/social/twitter.ts
@@ -1,16 +1,11 @@
-import { Injectable } from '@angular/core';
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/do';
-import * as _ from 'lodash';
-
-import ENV from '@environment';
-
+import { Injectable } from '@angular/core';
 import { SocialProvider, Post } from './social';
 import { HttpClient } from '@angular/common/http';
-import { Platform } from 'ionic-angular';
 import { chooseEndpoint } from '../../utils';
 import { Observable } from 'rxjs/Observable';
-import { from } from 'rxjs/observable/from';
+import ENV from '@environment';
 
 const ENDPOINT = chooseEndpoint('/twitter', 'https://api.twitter.com/1.1');
 
@@ -22,9 +17,7 @@ const ENDPOINT = chooseEndpoint('/twitter', 'https://api.twitter.com/1.1');
 */
 @Injectable()
 export class TwitterProvider implements SocialProvider {
-
-  private endpoint: string;
-  constructor(private http: HttpClient, private platform: Platform) {
+  constructor(private http: HttpClient) {
   }
 
   public getPosts(handle: string, args?): Observable<Post[]> {

From 2d3fded5c4886c72b3245e25ba71266b4b684e77 Mon Sep 17 00:00:00 2001
From: Amitosh Swain Mahapatra <amitosh.swain@gmail.com>
Date: May 30 2018 07:02:31 +0000
Subject: [PATCH 4/4] Add documentation to providers and pages


---

diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a115f57..67a0859 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -9,18 +9,24 @@ import { WomenPage } from '../pages/women/women';
 import { FirstPage } from '../pages/first/first';
 import { SplashScreen } from '@ionic-native/splash-screen';
 
+/**
+ * Entrypoint for the Fedora App
+ */
 @Component({
   templateUrl: 'app.html',
 })
 export class App {
+  /**
+   * Contains the pages that constitute this app
+   */
   pages: { title: string, component: any }[];
+
   rootPage: any;
 
   @ViewChild('content') nav: NavController;
 
 
   constructor(platform: Platform, splashScreen: SplashScreen) {
-    // used for an example of ngFor and navigation
     this.pages = [
       { title: 'Home', component: FirstPage },
       { title: 'Magazine', component: MagazinePage },
@@ -40,9 +46,14 @@ export class App {
     });
   }
 
-  openPage(page) {
-    // Reset the content nav to have just this page
-    // we wouldn't want the back button to show in this scenario
+  /**
+   * Navigate to a new page
+   *
+   * @param page page to navigate to
+   */
+  openPage(page): void {
+    // Reset the content nav to have just this page we wouldn't want the back
+    // button to show in this scenario
     this.nav.push(page.component);
   }
 }
diff --git a/src/app/config.env.ts.example b/src/app/config.env.ts.example
index 19cfb63..8b44a9b 100644
--- a/src/app/config.env.ts.example
+++ b/src/app/config.env.ts.example
@@ -8,7 +8,8 @@ const FB_CONFIG = {
 };
 
 const TWITTER_CONFIG = {
-  BEARER_TOKEN: 'AAAAAEXAMPLEAAAAAAAAMmWwEXAMPLE9tGRvIFnIR8XYXmIFTFaEGagjX0%3Dup2JDIi9hjbCJKGaEGDqkLMtYSGumkyMa6SbwXx0FMB1vOlvN0'
+  BEARER_TOKEN: 'AAAAAEXAMPLEAAAAAAAAMmWwEXAMPLE9tGRvIFnIR8XYXmEXAMpLEGgjX0%3' +
+                'Dup2JDExAmplEJKGaEXAmpLetYSGumkyExAMplEx0FMB1vOlvN0'
 };
 
 const ENV = {
diff --git a/src/app/main.ts b/src/app/main.ts
index 060f649..059feec 100644
--- a/src/app/main.ts
+++ b/src/app/main.ts
@@ -4,6 +4,8 @@ import { AppModule } from './app.module';
 import { enableProdMode } from '@angular/core';
 
 if (ENV.PROD) {
+  // Enable Angular prod mode in PROD builds only. Angular Prod mode drops a
+  // considerable amount of assertions
   enableProdMode();
 }
 
diff --git a/src/pages/ask/ask.html b/src/pages/ask/ask.html
index ba6ba7a..be8ca8a 100644
--- a/src/pages/ask/ask.html
+++ b/src/pages/ask/ask.html
@@ -39,17 +39,17 @@
       </ion-card-content>
       <ion-row id="stats">
         <ion-col id="vote">
-          <strong>{{ question.vote }}</strong>
+          <strong>{{ question.score }}</strong>
           <br>
           VOTE
         </ion-col>
         <ion-col id="ans">
-          <strong>{{ question.answers }}</strong>
+          <strong>{{ question.answerCount }}</strong>
           <br>
           Ans
         </ion-col>
         <ion-col id="view">
-          <strong>{{ question.view }}</strong>
+          <strong>{{ question.viewCount }}</strong>
           <br>
           VIEW
         </ion-col>
diff --git a/src/pages/ask/ask.ts b/src/pages/ask/ask.ts
index e61edc1..ce42a88 100644
--- a/src/pages/ask/ask.ts
+++ b/src/pages/ask/ask.ts
@@ -1,21 +1,24 @@
 import { Component } from '@angular/core';
 import { SocialSharing } from '@ionic-native/social-sharing';
-
 import { Browser } from '../../providers/browser/browser';
-import { AskFedoraService } from '../../providers/ask-fedora/ask-fedora';
-
-/*
-  Generated class for the AskPage page.
+import { AskFedoraService, Question } from '../../providers/ask-fedora/ask-fedora';
 
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
+/**
+ * Shows latest questions from Ask Fedora
+ *
+ * Fetches latest 30 questions from Ask Fedora and displays the questions, the
+ * answers and the number of votes each question receives.
+ */
 @Component({
   templateUrl: 'ask.html',
   providers: [Browser, AskFedoraService],
 })
 export class AskPage {
-  private questions: any;
+
+  /**
+   * Stores list of displayed questions
+   */
+  private questions: Question[];
 
   constructor(private browser: Browser,
     private askFedora: AskFedoraService, private socialSharing: SocialSharing) {
@@ -26,7 +29,10 @@ export class AskPage {
     this.updateQuestions();
   }
 
-  updateQuestions() {
+  /**
+   * Fetch a list of latest questions using Ask Fedora API.
+   */
+  updateQuestions(): void {
     this.askFedora
       .getQuestions()
       .subscribe(questions => {
@@ -34,16 +40,31 @@ export class AskPage {
       });
   }
 
-  openQuestion(event) {
-    this.browser.open(event.link);
+  /**
+   * Open a question in a browser.
+   *
+   * Opens question in an in-app browser in mobile app and in a new tab on desktop.
+   *
+   * @param question question to open
+   */
+  openQuestion(question: Question): void {
+    this.browser.open(question.link);
   }
 
-  shareQuestion(event) {
+  /**
+   * Share the question using a third-party app installed in the user's device
+   *
+   * Allows to share the question using apps like WhatsApp, Facebook, or any app that
+   * exposes a share interface to the underlying OS.
+   *
+   * @param question question to share
+   */
+  shareQuestion(question: Question): void {
     this.socialSharing.share(
-      event.title,
-      event.title,
+      question.content,
+      question.title,
       null,
-      event.link
+      question.link
     );
   }
 }
diff --git a/src/pages/calendar/calendar.ts b/src/pages/calendar/calendar.ts
index 1ec2f2b..45f372e 100644
--- a/src/pages/calendar/calendar.ts
+++ b/src/pages/calendar/calendar.ts
@@ -1,24 +1,39 @@
 import { Component } from '@angular/core';
 import { Calendar } from '@ionic-native/calendar';
 
-import { FedoCalService, Calendar as CalendarType } from '../../providers/fedo-cal/fedo-cal';
+import { FedoCalService, Calendar as CalendarType, Meeting } from '../../providers/fedo-cal/fedo-cal';
 
-/*
-  Generated class for the CalendarPage page.
-
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
 
+/**
+ * We default to the QA calendar
+ */
 const DEFAULT_CALENDAR = 'QA';
 
+/**
+ * The FedoCal interface
+ *
+ * Shows the list of calendars availabe on FedoCal and the meetings of each
+ * calendar. Also allows to add meetings from the calendar to the system calendar.
+ */
 @Component({
   templateUrl: 'calendar.html',
   providers: [FedoCalService, Calendar]
 })
 export class CalendarPage {
+
+  /**
+   * List of calendars in FedoCal
+   */
   private calendars: CalendarType[];
-  private meetings: Array<any>;
+
+  /**
+   * List of meetings in the selected calendar
+   */
+  private meetings: Meeting[];
+
+  /**
+   * ID of the selected calendar
+   */
   private selectedCalendar: string;
 
   constructor(private fedoCal: FedoCalService, private calendar: Calendar) {
@@ -33,7 +48,10 @@ export class CalendarPage {
     this.updateMeetings();
   }
 
-  updateCalendars() {
+  /**
+   * Update the list of calendars from FedoCal
+   */
+  updateCalendars(): void {
     this.fedoCal
       .getCalendars()
       .subscribe(calendars => {
@@ -41,7 +59,10 @@ export class CalendarPage {
       });
   }
 
-  updateMeetings() {
+  /**
+   * Update the list of meetings for the selected calendar
+   */
+  updateMeetings(): void {
     this.fedoCal
       .getMeetings(this.selectedCalendar)
       .subscribe(meetings => {
@@ -49,13 +70,18 @@ export class CalendarPage {
       });
   }
 
-  addToCalendar(event) {
+  /**
+   * Add a FedoCal meeting to the system calendar
+   *
+   * @param meeting meeting to add to the calendar
+   */
+  addToCalendar(meeting:Meeting): void {
     this.calendar.createEventInteractively(
-      event.name,
-      event.location,
-      event.real_description,
-      event.datetime_start,
-      event.datetime_end
+      meeting.name,
+      meeting.location,
+      meeting.description,
+      meeting.time,
+      meeting.timeEnd
     );
   }
 }
diff --git a/src/pages/first/first.ts b/src/pages/first/first.ts
index ed1f25e..6a8b464 100644
--- a/src/pages/first/first.ts
+++ b/src/pages/first/first.ts
@@ -1,46 +1,47 @@
 import { Component } from '@angular/core';
 import { NavController } from 'ionic-angular';
-import { Browser } from '../../providers/browser/browser';
 import { MagazinePage } from '../magazine/magazine';
 import { AskPage } from '../ask/ask';
 import { CalendarPage } from '../calendar/calendar';
 import { SocialPage } from '../social/social';
 
-/*
-  Generated class for the MagazinePage page.
-
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
+/**
+ * Home page of the Fedora App
+ */
 @Component({
   templateUrl: 'first.html',
 })
 
 export class FirstPage {
-  private posts: Array<any>;
-
-  constructor(private nav: NavController, private browser: Browser) {
-    this.posts = [];
-  }
 
-  openPost(event) {
-    this.browser.open(event.link);
+  constructor(private nav: NavController) {
   }
 
-  openMag() {
+  /**
+   * Navigate to Fedora Magazine section
+   */
+  openMag(): void {
     this.nav.push(MagazinePage);
   }
-  openAsk() {
+
+  /**
+   * Navigate to Ask Fedora section
+   */
+  openAsk(): void {
     this.nav.push(AskPage);
   }
-  openSocial() {
+
+  /**
+   * Navigate to Fedora Social section
+   */
+  openSocial(): void {
     this.nav.push(SocialPage);
   }
-  openCal() {
-    this.nav.push(CalendarPage);
-  }
-
-  login(event) {
 
+  /**
+   * Navigate to Fedora Calendar section
+   */
+  openCal(): void {
+    this.nav.push(CalendarPage);
   }
 }
diff --git a/src/pages/magazine/magazine.ts b/src/pages/magazine/magazine.ts
index b0c724f..172d1ca 100644
--- a/src/pages/magazine/magazine.ts
+++ b/src/pages/magazine/magazine.ts
@@ -4,17 +4,18 @@ import { SocialSharing } from '@ionic-native/social-sharing';
 import { Browser } from '../../providers/browser/browser';
 import { FedoraMagazineService, Post } from '../../providers/fedora-magazine/fedora-magazine';
 
-/*
-  Generated class for the MagazinePage page.
-
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
+/**
+ * Shows latest posts from Fedora Magazine
+ */
 @Component({
   templateUrl: 'magazine.html',
   providers: [FedoraMagazineService],
 })
 export class MagazinePage {
+
+  /**
+   * List of posts from Fedora Magazine
+   */
   private posts: Post[];
 
   constructor(private browser: Browser,
@@ -26,21 +27,39 @@ export class MagazinePage {
     this.updatePosts();
   }
 
-  updatePosts() {
+  /**
+   * Fetch latest posts from Fedor Magazine API
+   */
+  updatePosts(): void {
     this.fedoraMag.getPosts()
       .subscribe(posts => {
         this.posts = posts;
       });
   }
 
-  openPost(event) {
-    this.browser.open(event.link);
+  /**
+   * Open a post in a browser
+   *
+   * Opens the post in an in-app browser in mobile app and in a new tab on desktop.
+   *
+   * @param post post to open
+   */
+  openPost(post:Post): void {
+    this.browser.open(post.link);
   }
 
-  sharePost(event) {
+  /**
+   * Share the post using a third-party app installed in the user's device
+   *
+   * Allows to share the post using apps like WhatsApp, Facebook, or any app that
+   * exposes a share interface to the underlying OS.
+   *
+   * @param post post to share
+   */
+  sharePost(post:Post): void {
     this.socialSharing.share(
-      event.title, event.title,
-      null, event.link
+      post.excerpt, post.title,
+      null, post.permalink
     );
   }
 }
diff --git a/src/pages/social/social.ts b/src/pages/social/social.ts
index 185dd8d..3f79a04 100644
--- a/src/pages/social/social.ts
+++ b/src/pages/social/social.ts
@@ -12,19 +12,22 @@ const HANDLE = {
   TWITTER: 'fedora_qa',
 };
 
-/*
-  Generated class for the SocialPage page.
+const DEFAULT_POST_TITLE = 'Update from Fedora Project';
 
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
+/**
+ * Shows updates from the social media channels of Fedora
+ */
 @Component({
   templateUrl: 'social.html',
   providers: [FacebookProvider, TwitterProvider],
 })
 export class SocialPage {
 
+  /**
+   * List of posts from different social media channels
+   */
   private posts: Post[];
+
   constructor(private browser: Browser, private fb: FacebookProvider, private twitter: TwitterProvider,
     private socialSharing: SocialSharing) {
     this.posts = [];
@@ -34,23 +37,45 @@ export class SocialPage {
     this.updatePosts();
   }
 
-  private updatePosts() {
+  /**
+   * Fetch posts from social media channels
+   *
+   * Currently, we fetch posts from Facebook and Twitter
+   */
+  private updatePosts(): void {
     forkJoin(this.fb.getPosts(HANDLE.FB), this.twitter.getPosts(HANDLE.TWITTER))
       .subscribe(values => {
         this.posts = [...values[0], ...values[1]] as Post[];
       });
   }
 
-  openPost(event) {
-    this.browser.open(event.link);
+  /**
+   * Open a post in a browser
+   *
+   * Opens the post in an in-app browser in mobile app and in a new tab on desktop.
+   * On some platforms, the post may directly open in the app of the social media
+   * service.
+   *
+   * @param post post to open
+   */
+  openPost(post:Post): void {
+    this.browser.open(post.link);
   }
 
-  sharePost(event) {
+  /**
+   * Share the post using a third-party app installed in the user's device
+   *
+   * Allows to share the post using apps like WhatsApp, Facebook, or any app that
+   * exposes a share interface to the underlying OS.
+   *
+   * @param post post to share
+   */
+  sharePost(post:Post): void {
     this.socialSharing.share(
-      event.title,
-      event.title,
+      post.content,
+      DEFAULT_POST_TITLE,
       null,
-      event.link
+      post.link
     );
   }
 }
diff --git a/src/pages/women/women.ts b/src/pages/women/women.ts
index 7ef83df..0b037c9 100644
--- a/src/pages/women/women.ts
+++ b/src/pages/women/women.ts
@@ -1,11 +1,10 @@
 import { Component } from '@angular/core';
 
-/*
-  Generated class for the MagazinePage page.
-
-  See http://ionicframework.com/docs/v2/components/#navigation for more info on
-  Ionic pages and navigation.
-*/
+/**
+ * Fedora Women section
+ *
+ * Under construction
+ */
 @Component({
   templateUrl: 'women.html',
 })
diff --git a/src/providers/ask-fedora/ask-fedora.ts b/src/providers/ask-fedora/ask-fedora.ts
index 09afe00..1b62eef 100644
--- a/src/providers/ask-fedora/ask-fedora.ts
+++ b/src/providers/ask-fedora/ask-fedora.ts
@@ -2,45 +2,95 @@ import 'rxjs/add/operator/map';
 import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { chooseEndpoint } from '../../utils';
+import { Observable } from 'rxjs/Observable';
 
+/**
+ * Ask Fedora API endpoint
+ *
+ * It does not support CORS so we have to proxy it through Ionic CLI during development
+ */
 const ENDPOINT = chooseEndpoint('/ask-fedora', 'https://ask.fedoraproject.org/en/api/v1');
 
+/**
+ * A question on Ask Fedora
+ */
 export interface Question {
-  id: string,
+  /**
+   * Question ID
+   */
+  id: number,
+
+  /**
+   * Question title
+   */
   title: string,
+
+  /**
+   * Permalink to the question
+   */
   link: string,
-  answers: number,
+
+  /**
+   * Number of answers to this question
+   */
+  answerCount: number,
+
+  /**
+   * Content of this question
+   */
   content: string,
-  timestamp: Date,
+
+  /**
+   * Time of posting of this question
+   */
+  addedAt: Date,
+
+  /**
+   * Tags associated with this question
+   */
   tags: string[],
-  view: number,
-  vote: number,
-}
 
+  /**
+   * Number of views registered by this question
+   */
+  viewCount: number,
+
+  /**
+   * Total score for this question
+   *
+   * It is the sum of all up-votes and down-votes.
+   */
+  score: number,
+}
 
-/*
-  Generated class for the AskFedora provider.
 
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
+/**
+ * Service for Ask Fedora API
+ *
+ * Provides a read-only access to questions and answers posted in Ask Fedora.
+ */
 @Injectable()
 export class AskFedoraService {
   constructor(private http: HttpClient) {
   }
 
-  getQuestions() {
+  /**
+   * Fetch a list of questions from Ask Fedora API
+   *
+   * @returns Observable which emits an array of questions
+   */
+  getQuestions(): Observable<Question[]> {
     return this.http.get(`${ENDPOINT}/questions/`)
-      .map((data: any) => data.questions.map(q => ({
+      .map((data: any) => (data.questions as any[]).map(q => ({
         id: q.id,
         title: q.title,
         link: q.url,
-        answers: q.answer_count,
+        answerCount: q.answer_count,
         content: q.summary,
-        timestamp: q.added_at,
+        addedAt: new Date(parseInt(q.added_at, 10)),
         tags: q.tags,
-        view: q.view_count,
-        vote: q.score,
+        viewCount: q.view_count,
+        score: q.score,
       })));
   }
 }
diff --git a/src/providers/browser/browser.ts b/src/providers/browser/browser.ts
index 2b3715d..b038007 100644
--- a/src/providers/browser/browser.ts
+++ b/src/providers/browser/browser.ts
@@ -4,28 +4,36 @@ import { SpinnerDialog } from '@ionic-native/spinner-dialog';
 import { Toast } from '@ionic-native/toast';
 import { Platform } from 'ionic-angular';
 
-/*
-  Generated class for the Browser provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
+/**
+ * Wrapper over in-app browser to show a busy indicator while loading web content
+ */
 @Injectable()
 export class Browser {
+
   private browser: InAppBrowserObject;
+
   constructor(private platform: Platform, private inAppBrowser: InAppBrowser, private spinnerDialog: SpinnerDialog,
     private toast: Toast) {
     this.browser = null;
   }
 
+  /**
+   * Called when content loading is started
+   */
   private startHandler = () => {
     this.spinnerDialog.show();
   }
 
+  /**
+   * Called when content loading is complete
+   */
   private stopHandler = () => {
     this.spinnerDialog.hide();
   }
 
+  /**
+   * Called when there is an error while loading content
+   */
   private errorHandler = (error) => {
     this.toast.showShortBottom('Something went wrong.');
     this.browser.close();
@@ -33,8 +41,15 @@ export class Browser {
     this.browser = null;
   }
 
-  open(link) {
-    const browser: InAppBrowserObject = this.inAppBrowser.create(encodeURI(link), '_blank');
+  /**
+   * Open a link in an in-app browser
+   *
+   * Attaches lifecycle callbacks only on platforms which support them.
+   *
+   * @param link link to open in a an in-app browser
+   */
+  public open(link): void {
+    const browser = this.inAppBrowser.create(encodeURI(link), '_blank');
     this.browser = browser;
 
     // Cordova does not fire the events in browser.
diff --git a/src/providers/fedo-cal/fedo-cal.ts b/src/providers/fedo-cal/fedo-cal.ts
index ce0c7bb..f753d4a 100644
--- a/src/providers/fedo-cal/fedo-cal.ts
+++ b/src/providers/fedo-cal/fedo-cal.ts
@@ -8,15 +8,31 @@ 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';
+
+/**
+ * Time format for display
+ */
 const TIME_FORMAT = 'h:mm A z';
 
-/*
-  Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n
-*/
+/**
+ * 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':
@@ -28,43 +44,114 @@ function calendarNameToDisplayName(name: string): string {
   }
 }
 
+/**
+ * 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,
 }
 
+/**
+ * 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,
   }
 }
 
-/*
-  Generated class for the FedoCal provider.
-
-  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 FedoCalService {
   constructor(private http: HttpClient) {
   }
 
+  /**
+   * 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) =>
@@ -79,7 +166,13 @@ export class FedoCalService {
         })));
   }
 
-  getMeetings(calendar): Observable<Meeting[]> {
+  /**
+   * 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 => {
@@ -92,6 +185,7 @@ export class FedoCalService {
           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)
           },
@@ -105,8 +199,15 @@ export class FedoCalService {
   }
 }
 
-
-function dateToMoment(date, time, timezone) {
+/**
+ * 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;
 }
diff --git a/src/providers/fedora-magazine/fedora-magazine.ts b/src/providers/fedora-magazine/fedora-magazine.ts
index df22c3b..8b52bd9 100644
--- a/src/providers/fedora-magazine/fedora-magazine.ts
+++ b/src/providers/fedora-magazine/fedora-magazine.ts
@@ -6,60 +6,75 @@ import { chooseEndpoint } from '../../utils';
 
 const ENDPOINT = chooseEndpoint('/fedora-magazine', 'https://fedoramagazine.org/wp-json/wp/v2');
 
+/**
+ * Represents a post on Fedora Magazine
+ */
 export interface Post {
+  /**
+   * Unique ID of the post, supplied by the CMS
+   */
   id: number,
+
+  /**
+   * A sluggified link to the post
+   */
   link: string,
+
+  /**
+   * Permalink to the post
+   */
+  permalink:string,
+
+  /**
+   * Post title
+   */
   title: string,
-  image: any,
+
+  /**
+   * URL to the featured image of the post
+   */
+  image: string,
+
+  /**
+   * A short excerpt of the post
+   */
   excerpt: string,
+
+  /**
+   * The content of the post
+   */
   content: string,
-  date: Date
-}
 
-/*
-  Generated class for the FedoraMag provider.
+  /**
+   * Time of publication
+   */
+  publishedAt: Date
+}
 
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
+/**
+ * Service for fetching posts from Fedora Magazine API
+ */
 @Injectable()
 export class FedoraMagazineService {
   constructor(private http: HttpClient) {
   }
 
+  /**
+   * Fetch the list of latest posts on Fedora Magazine
+   *
+   * @returns Observable which emits an array of posts
+   */
   getPosts(): Observable<Post[]> {
     return this.http.get(`${ENDPOINT}/posts`)
       .map((data: any[]) => data.map((post: any) => ({
         id: post.id,
         link: post.link,
+        permalink: post.guid.rendered,
         title: post.title.rendered,
         image: post.featured_media,
         excerpt: post.excerpt.rendered,
         content: post.content.rendered,
-        date: new Date(post.date_gmt+'Z'),
+        publishedAt: new Date(post.date_gmt+'Z'),
       })));
   }
 }
-
-  /*  getMedia_url(posts) {
-      if (!_.isEmpty(this.media_url[posts])) {
-        return Promise.resolve(this.media_url[posts]);
-      }
-
-      return new Promise((resolve, reject) => {
-        this.request.get(this.API.media_url,{ posts : posts })
-          .then(data => {
-            this.media_url[posts] = _.map(data, i => {
-            //  var excerpt = p.excerpt.rendered;
-              var img_url = {
-                img_id: i.id,
-                img_link: i.guid.rendered,
-              };
-              //image_url = func_url(post.image);
-              return post;
-            });
-
-            return resolve(this.posts);
-          }).catch(reject);
-      });
-    }*/
diff --git a/src/providers/social/facebook.ts b/src/providers/social/facebook.ts
index 48e4ea0..a3192b4 100644
--- a/src/providers/social/facebook.ts
+++ b/src/providers/social/facebook.ts
@@ -7,20 +7,30 @@ import { SocialProvider, Post, FACEBOOK } from './social';
 import { Observable } from 'rxjs/Observable';
 import { Observer } from 'rxjs/Observer';
 
-/*
-  Generated class for the Fb provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
+/**
+ * Service for Facebook API
+ *
+ * Allows to fetch data from Facebook pages.
+ */
 @Injectable()
 export class FacebookProvider implements SocialProvider {
+
+  /**
+   * Facebook API instance
+   */
   private fb: Facebook;
   constructor() {
     this.fb = new Facebook(ENV.FB_CONFIG);
   }
 
-  private api(uri) {
+  /**
+   * Perform a call to a Facebook API using the library
+   *
+   * Converts callback-style error handling to observables.
+   *
+   * @param uri Facebook API URI
+   */
+  private api(uri:string) {
     return Observable.create((emitter: Observer<any>) => {
       this.fb.api(uri, res => {
         if (!res || res.error) {
@@ -33,6 +43,13 @@ export class FacebookProvider implements SocialProvider {
     });
   }
 
+  /**
+   * Fetch the list of posts for a given Facebook page
+   *
+   * @param page ID of the Facebook page
+   * @param args Extra args for pagination etc.
+   * @returns Observable which emits an array of Facebook posts
+   */
   public getPosts(page: string, args?): Observable<Post[]> {
     return this.api(`${page}/posts`)
       .map(res => {
diff --git a/src/providers/social/social.ts b/src/providers/social/social.ts
index 8832ffe..10b17b7 100644
--- a/src/providers/social/social.ts
+++ b/src/providers/social/social.ts
@@ -1,19 +1,51 @@
 import { Observable } from "rxjs/Observable";
 
+/**
+ * ID for facebook provider
+ */
 export const FACEBOOK = 'facebook';
+
+/**
+ * ID for twitter provider
+ */
 export const TWITTER = 'twitter';
 
+/**
+ * Represents a post in social media service
+ */
 export interface Post {
+  /**
+   * Service specific unique ID
+   */
   id:string,
+
+  /**
+   * Origin of the post
+   */
   origin:string,
+
+  /**
+   * Link to the app of website of the social media service hosting the post
+   */
   link:string,
-  content:string,
-}
 
-export interface GetPostExtraArgs {
-  offset?: string,
+  /**
+   * Text content of the post
+   */
+  content:string,
 }
 
+/**
+ * A generic social media provider
+ */
 export interface SocialProvider {
-  getPosts(resID: string, args?:GetPostExtraArgs): Observable<Post[]>
+
+  /**
+   * Fetch the list of posts for a given service specific resource ID
+   *
+   * @param resID service specific resource IS
+   * @param args Extra args for pagination etc.
+   * @returns Observable which emits an array of posts
+   */
+  getPosts(resID: string, args?:{ offset?: string }): Observable<Post[]>
 }
diff --git a/src/providers/social/twitter.ts b/src/providers/social/twitter.ts
index 490c566..444b7d6 100644
--- a/src/providers/social/twitter.ts
+++ b/src/providers/social/twitter.ts
@@ -9,17 +9,21 @@ import ENV from '@environment';
 
 const ENDPOINT = chooseEndpoint('/twitter', 'https://api.twitter.com/1.1');
 
-/*
-  Generated class for the Twitter provider.
-
-  See https://angular.io/docs/ts/latest/guide/dependency-injection.html
-  for more info on providers and Angular 2 DI.
-*/
+/**
+ * Service for Twitter API
+ */
 @Injectable()
 export class TwitterProvider implements SocialProvider {
   constructor(private http: HttpClient) {
   }
 
+  /**
+   * Fetch the list of tweets for a given Twitter handle
+   *
+   * @param handle Twitter handle whose tweets to load
+   * @param args   Extra args for pagination etc.
+   * @returns Observable which emits an array of tweets
+   */
   public getPosts(handle: string, args?): Observable<Post[]> {
     return this.http.get(`${ENDPOINT}/statuses/user_timeline.json`, {
       params: { screen_name: handle },
diff --git a/src/utils.ts b/src/utils.ts
index 25bc712..77c0b48 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,8 +1,9 @@
 
 /**
  * Choose appropriate API end point or the proxy to avoid CORS in Ionic dev server
+ *
  * @param devServerEndpoint endpoint in Ionic dev server
- * @param appEndpoint endpoint to use in real application
+ * @param appEndpoint       endpoint to use in real application
  */
 export function chooseEndpoint(devServerEndpoint:string, appEndpoint:string) {
   // courtesy: https://forum.ionicframework.com/t/check-if-run-on-emulator-dev-production-or-livereload/71845/9

