#57 Add type annotations where necessary
Merged 7 years ago by a2batic. Opened 7 years ago by amitosh.
amitosh/Fedora-app tsify  into  master

file modified
+4 -8
@@ -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/"

      }

    ],

file modified
+5
@@ -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",

file modified
+1
@@ -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",

file modified
+27 -16
@@ -1,26 +1,32 @@ 

- 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';

  

+ /**

+  * Entrypoint for the Fedora App

+  */

  @Component({

    templateUrl: 'app.html',

  })

- export class MyApp {

-   pages:Array<{title:string, component:any}>;

-   rootPage:any;

+ export class App {

+   /**

+    * Contains the pages that constitute this app

+    */

+   pages: { title: string, component: any }[];

  

-   @ViewChild('content') nav:NavController;

+   rootPage: any;

  

+   @ViewChild('content') nav: NavController;

  

-   constructor(platform:Platform, splashScreen:SplashScreen) {

-     // used for an example of ngFor and navigation

+ 

+   constructor(platform: Platform, splashScreen: SplashScreen) {

      this.pages = [

        { title: 'Home', component: FirstPage },

        { title: 'Magazine', component: MagazinePage },
@@ -40,9 +46,14 @@ 

Shouldnt it be containing all the pages ?

      });

    }

  

-   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);

    }

  }

file modified
+5 -9
@@ -9,7 +9,7 @@ 

  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';
@@ -18,12 +18,10 @@ 

  import { WomenPage } from '../pages/women/women';

  

  import { Browser } from '../providers/browser/browser';

- import { Request } from '../providers/request/request';

- import { IonicConfig } from '../providers/ionic-config/ionic-config';

  

  @NgModule({

    declarations: [

-     MyApp,

+     App,

      FirstPage,

      MagazinePage,

      AskPage,
@@ -34,11 +32,11 @@ 

    imports: [

      BrowserModule,

      HttpClientModule,

-     IonicModule.forRoot(MyApp)

+     IonicModule.forRoot(App)

    ],

    bootstrap: [IonicApp],

    entryComponents: [

-     MyApp,

+     App,

      FirstPage,

      MagazinePage,

      AskPage,
@@ -48,15 +46,13 @@ 

    ],

    providers: [

      Browser,

-     IonicConfig,

      InAppBrowser,

-     Request,

      SocialSharing,

      SplashScreen,

      SpinnerDialog,

      StatusBar,

      Toast,

-     { provide: ErrorHandler, useClass: IonicErrorHandler }

+     { provide: ErrorHandler, useClass: IonicErrorHandler },

    ]

  })

  export class AppModule { }

@@ -8,7 +8,8 @@ 

  };

  

  const TWITTER_CONFIG = {

-   BEARER_TOKEN: 'AAAAAEXAMPLEAAAAAAAAMmWwEXAMPLE9tGRvIFnIR8XYXmIFTFaEGagjX0%3Dup2JDIi9hjbCJKGaEGDqkLMtYSGumkyMa6SbwXx0FMB1vOlvN0'

+   BEARER_TOKEN: 'AAAAAEXAMPLEAAAAAAAAMmWwEXAMPLE9tGRvIFnIR8XYXmEXAMpLEGgjX0%3' +

+                 'Dup2JDExAmplEJKGaEXAmpLetYSGumkyExAMplEx0FMB1vOlvN0'

  };

  

  const ENV = {

file modified
+2
@@ -4,6 +4,8 @@ 

  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();

  }

  

file modified
+3 -3
@@ -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>

file modified
+40 -19
@@ -1,24 +1,27 @@ 

  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';

- 

- /*

-   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, AskFedora],

+   providers: [Browser, AskFedoraService],

  })

  export class AskPage {

-   private questions: any;

+ 

+   /**

+    * Stores list of displayed questions

+    */

+   private questions: Question[];

  

    constructor(private browser: Browser,

-     private askFedora: AskFedora, private socialSharing: SocialSharing) {

+     private askFedora: AskFedoraService, private socialSharing: SocialSharing) {

      this.questions = [];

    }

  
@@ -26,24 +29,42 @@ 

      this.updateQuestions();

    }

  

-   updateQuestions() {

+   /**

+    * Fetch a list of latest questions using Ask Fedora API.

+    */

+   updateQuestions(): void {

      this.askFedora

        .getQuestions()

-       .then(questions => {

+       .subscribe(questions => {

          this.questions = questions;

        });

    }

  

-   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

      );

    }

  }

@@ -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)">

file modified
+48 -22
@@ -1,27 +1,42 @@ 

  import { Component } from '@angular/core';

  import { Calendar } from '@ionic-native/calendar';

  

- import { FedoCal } 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: [ FedoCal, Calendar ]

+   providers: [FedoCalService, Calendar]

  })

  export class CalendarPage {

-   private calendars:Array<any>;

-   private meetings:Array<any>;

-   private selectedCalendar:string;

  

-   constructor(private fedoCal:FedoCal, private calendar:Calendar) {

+   /**

+    * List of calendars in FedoCal

+    */

+   private calendars: CalendarType[];

+ 

+   /**

+    * 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) {

      this.calendars = [];

      this.meetings = [];

  
@@ -33,29 +48,40 @@ 

      this.updateMeetings();

    }

  

-   updateCalendars() {

+   /**

+    * Update the list of calendars from FedoCal

+    */

+   updateCalendars(): void {

      this.fedoCal

        .getCalendars()

-       .then(calendars => {

+       .subscribe(calendars => {

          this.calendars = calendars;

        });

    }

  

-   updateMeetings() {

+   /**

+    * Update the list of meetings for the selected calendar

+    */

+   updateMeetings(): void {

      this.fedoCal

        .getMeetings(this.selectedCalendar)

-       .then(meetings => {

+       .subscribe(meetings => {

          this.meetings = meetings;

        });

    }

  

-   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

      );

    }

  }

file modified
+23 -44
@@ -1,68 +1,47 @@ 

  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.

- 

-   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',

-   providers: [ FedoraMag ]

  })

  

  export class FirstPage {

-   private 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);

+   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() {

-     this.nav.push(SocialPage);

-   }

-   openCal() {

-     this.nav.push(CalendarPage);

-   }

- 

-   login(event) {

  

+   /**

+    * Navigate to Fedora Social section

+    */

+   openSocial(): void {

+     this.nav.push(SocialPage);

    }

  

-   sharePost(event) {

-     this.socialSharing.share(

-       event.title, event.title,

-       null, event.link

-     );

+   /**

+    * Navigate to Fedora Calendar section

+    */

+   openCal(): void {

+     this.nav.push(CalendarPage);

    }

  }

file modified
+40 -20
@@ -2,23 +2,24 @@ 

  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.

- 

-   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: [FedoraMag],

+   providers: [FedoraMagazineService],

  })

  export class MagazinePage {

-   private posts:Array<any>;

  

-   constructor(private browser:Browser,

-     private fedoraMag:FedoraMag, private socialSharing:SocialSharing) {

+   /**

+    * List of posts from Fedora Magazine

+    */

+   private posts: Post[];

+ 

+   constructor(private browser: Browser,

+     private fedoraMag: FedoraMagazineService, private socialSharing: SocialSharing) {

      this.posts = [];

    }

  
@@ -26,20 +27,39 @@ 

      this.updatePosts();

    }

  

-   updatePosts() {

-     this.fedoraMag.getPosts().then(posts => {

-       this.posts = posts;

-     });

+   /**

+    * 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

      );

    }

  }

file modified
+6 -6
@@ -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>

file modified
+53 -48
@@ -2,75 +2,80 @@ 

  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';

+ import { forkJoin } from 'rxjs/observable/forkJoin';

  

- /*

-   Generated class for the SocialPage page.

+ const HANDLE = {

+   FB: 'fedoraqa',

+   TWITTER: 'fedora_qa',

+ };

  

-   See http://ionicframework.com/docs/v2/components/#navigation for more info on

-   Ionic pages and navigation.

- */

+ const DEFAULT_POST_TITLE = 'Update from Fedora Project';

+ 

+ /**

+  * Shows updates from the social media channels of Fedora

+  */

  @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 updates:Array<any>;

-   private USER:any;

+   /**

+    * List of posts from different social media channels

+    */

+   private posts: Post[];

  

-   constructor(private browser:Browser, private fb:FB, private tw:Tw,

-               private socialSharing:SocialSharing) {

+   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

-       .getPagePosts(this.USER.FB)

-       .then((posts:Array<any>) => {

-         this.posts = posts;

-         this.mergeUpdates();

-       });

- 

-     this.tw

-       .getTimelineTweets(this.USER.TW)

-       .then((tweets:Array<any>) => {

-         this.tweets = tweets;

-         this.mergeUpdates();

+   /**

+    * 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[];

        });

    }

  

-   mergeUpdates() {

-     // TODO: Merge as per ascending order of timestamps?

-     this.updates = [ ...this.posts, ...this.tweets ];

-   }

- 

-   openUpdate(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);

    }

  

-   shareUpdate(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

      );

    }

  }

file modified
+5 -60
@@ -1,67 +1,12 @@ 

  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.

- 

-   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',

-   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

-     );

-   }

  }

@@ -1,57 +1,96 @@ 

- import { Injectable } from '@angular/core';

  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';

  

- import * as _ from 'lodash';

+ /**

+  * 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');

  

- import { Request } from '../request/request';

+ /**

+  * A question on Ask Fedora

+  */

+ export interface Question {

+   /**

+    * Question ID

+    */

+   id: number,

  

- const API_ENDPOINT = 'https://ask.fedoraproject.org/en/api/v1';

+   /**

+    * Question title

+    */

+   title: string,

  

- /*

-   Generated class for the AskFedora provider.

+   /**

+    * Permalink to the question

+    */

+   link: string,

  

-   See https://angular.io/docs/ts/latest/guide/dependency-injection.html

-   for more info on providers and Angular 2 DI.

- */

- @Injectable()

- export class AskFedora {

-   private API:any;

-   private questions:Array<any>;

+   /**

+    * Number of answers to this question

+    */

+   answerCount: number,

+ 

+   /**

+    * Content of this question

+    */

+   content: string,

+ 

+   /**

+    * Time of posting of this question

+    */

+   addedAt: Date,

  

-   constructor(private request:Request) {

-     this.API = {

-       base: [API_ENDPOINT],

-       questions: [API_ENDPOINT, 'questions'],

-     };

+   /**

+    * Tags associated with this question

+    */

+   tags: string[],

  

-     this.questions = [];

+   /**

+    * 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,

+ }

+ 

+ 

+ /**

+  * 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() {

-     if (!_.isEmpty(this.questions)) {

-       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);

-     });

+   /**

+    * 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 as any[]).map(q => ({

+         id: q.id,

+         title: q.title,

+         link: q.url,

+         answerCount: q.answer_count,

+         content: q.summary,

+         addedAt: new Date(parseInt(q.added_at, 10)),

+         tags: q.tags,

+         viewCount: q.view_count,

+         score: q.score,

+       })));

    }

  }

@@ -4,28 +4,36 @@ 

  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 @@ 

      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.

file removed
-56
@@ -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);

-     });

-   }

- }

@@ -4,23 +4,37 @@ 

  import * as _ from 'lodash';

  import * as moment from 'moment-timezone';

  

- import { Request } from '../request/request';

- 

- const API_ENDPOINT = 'https://apps.fedoraproject.org/calendar/api/';

- const API = {

-   base: [API_ENDPOINT],

-   calendars: [API_ENDPOINT, 'calendars'],

-   meetings: [API_ENDPOINT, 'meetings'],

- };

- 

+ import { HttpClient } from '@angular/common/http';

+ import { Observable } from 'rxjs/Observable';

+ import { chooseEndpoint } from '../../utils';

+ 

+ /**

+  * FedoCal API endpoint

+  *

+  * It does not support CORS so we have to proxy it through Ionic CLI during development

+  */

+ const ENDPOINT = chooseEndpoint('/fedocal', 'https://apps.fedoraproject.org/calendar/api');

+ 

+ /**

+  * Date format for display

+  */

  const DATE_FORMAT = 'dddd, MMMM Do YYYY';

- const TIME_FORMAT = 'h:mm a z';

  

- /*

-   Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n

- */

- function calendarNameToDisplayName(name:string) :string {

-   switch(name) {

+ /**

+  * Time format for display

+  */

+ const TIME_FORMAT = 'h:mm A z';

+ 

+ /**

+  * Convert calendar name from API to a value suitable for display

+  *

+  * Convert title to capital case, but skip special names like FESCo, FAmSCo, i18n

+  *

+  * @param name Calendar name as in API

+  * @returns    Friendly representation of the name

+  */

+ function calendarNameToDisplayName(name: string): string {

+   switch (name) {

      case 'i18n':

        return 'i18n';

      case 'fesco':
@@ -28,93 +42,173 @@ 

      default:

        return /^[A-Z][^\d-]*.*$/.test(name) ? name : _.startCase(name);

    }

+ }

  

+ /**

+  * A calendar on FedoCal

+  *

+  * Calendars are assoicated with a group / SIG / or event. They contain a number

+  * of recurring meetings or events.

+  */

+ export interface Calendar {

+ 

+   /**

+    * Name of calendar, as expressed in the API

+    *

+    * Serves as an ID for API calls.

+    */

+   realName: string,

+ 

+   /**

+    * Human friendly calendar name obtained from `realName`

+    */

+   displayName: string,

+ 

+   /**

+    * Calendar's description

+    */

+   description: string,

+ 

+   /**

+    * Group with admin previlages for this calendar

+    */

+   adminGroup: string,

+ 

+   /**

+    * Group with edit previlages for this calendar

+    */

+   editorGroup: string,

+ 

+   /**

+    * Contact person for this calendar

+    */

+   contact: string,

+ 

+   /**

+    * Whether the calendar is enabled

+    */

+   enabled: boolean,

  }

  

- /*

-   Generated class for the FedoCal provider.

+ /**

+  * A meeting on a FedoCal calendar

+  */

+ export interface Meeting {

+ 

+   /**

+    * Name of the meeting

+    */

+   name: string,

+ 

+   /**

+    * Description of the meeting

+    */

+   description: string,

+ 

+   /**

+    * Where is the meeting happening?

+    *

+    * Can be an IRC channel or even a physical location.

+    */

+   location: string,

+ 

+   /**

+    * Meeting start time

+    */

+   time: Date,

+ 

+   /**

+    * Meeting end time

+    */

+   timeEnd: Date,

+ 

+   /**

+    * Start time formatted for display

+    */

+   displayTime: {

+     dateString: string,

+     timeString: string,

+   },

+ 

+   /**

+    * End time formatted for display

+    */

+   displayTimeEnd: {

+     dateString: string,

+     timeString: string,

+   }

+ }

  

-   See https://angular.io/docs/ts/latest/guide/dependency-injection.html

-   for more info on providers and Angular 2 DI.

- */

+ /**

+  * Service for FedoCal

+  */

  @Injectable()

- export class FedoCal {

-   private calendars:any;

-   private meetings:any;

- 

-   constructor(private request:Request) {

-     this.calendars = [];

-     this.meetings = [];

+ export class FedoCalService {

+   constructor(private http: HttpClient) {

    }

  

-   getCalendars() {

-     if (!_.isEmpty(this.calendars)) {

-       return Promise.resolve(this.calendars);

-     }

- 

-     return new Promise((resolve, reject) => {

-       this.request.get(API.calendars)

-         .then((data:any) => {

-           this.calendars = _.map(data.calendars, c => {

-             return {

-               real_name: c.calendar_name,

-               display_name: calendarNameToDisplayName(c.calendar_name),

-               description: c.calendar_description,

-               contact: c.calendar_contact,

-             };

-           });

- 

-           return resolve(this.calendars);

-         }).catch(reject);

-     });

+   /**

+    * Fetch the list of calendars from FedoCal API

+    *

+    * @returns Observable which emits an array of calendars

+    */

+   getCalendars(): Observable<Calendar[]> {

+     return this.http.get(`${ENDPOINT}/calendars/`)

+       .map((data: any) =>

+         data.calendars.map((c: any) => ({

+           realName: c.calendar_name,

+           displayName: calendarNameToDisplayName(c.calendar_name),

+           description: c.calendar_description,

+           contact: c.calendar_contact,

+           adminGroup: c.calendar_admin_group,

+           editorGroup: c.calendar_editor_group,

+           enabled: c.calendar_status === 'Enabled'

+         })));

    }

  

-   getMeetings(calendar) {

-     if (!_.isEmpty(this.meetings[calendar])) {

-       return Promise.resolve(this.meetings[calendar]);

-     }

- 

-     return new Promise((resolve, reject) => {

-       this.request.get(API.meetings, { calendar: calendar })

-         .then((data:any) => {

-           this.meetings[calendar] = _.map(data.meetings, m => {

-             const meeting:any = {

-               name: m.meeting_name,

-               real_description: m.meeting_information,

-               display_description: _.truncate(

-                 m.meeting_information,

-                 { length: 120, separator: ' ' }

-               ),

-               date_start: m.meeting_date,

-               time_start: m.meeting_time_start,

-               date_end: m.meeting_date_end,

-               time_end: m.meeting_time_end,

-               timezone: m.meeting_timezone,

-               location: m.meeting_location,

-             };

- 

-             const start = dateToMoment(meeting.date_start, meeting.time_start, meeting.timezone);

-             meeting.moment_start = start;

-             meeting.datetime_start = start.toDate();

- 

-             const end = dateToMoment(meeting.date_end, meeting.time_end, meeting.timezone);

-             meeting.moment_end = end;

-             meeting.datetime_end = end.toDate();

- 

-             meeting.display_date = start.format(DATE_FORMAT);

-             meeting.display_time = start.format(TIME_FORMAT);

- 

-             return meeting;

-           });

- 

-           return resolve(this.meetings[calendar]);

-         }).catch(reject);

-     });

+   /**

+    * Fetch the list of meetings for a given FedoCal calendar name

+    *

+    * @param calendar FedoCal calendar name

+    * @returns Observable which emits an array of meetings

+    */

+   getMeetings(calendar:string): Observable<Meeting[]> {

+ 

+     return this.http.get(`${ENDPOINT}/meetings/`, { params: { calendar: calendar } })

+       .map((data: any) => data.meetings.map(m => {

+         const mTime = dateToMoment(m.meeting_date, m.meeting_time_start, m.meeting_timezone);

+         const mTimeEnd = dateToMoment(m.meeting_date_end, m.meeting_time_stop, m.meeting_timezone);

+ 

+         return {

+           name: m.meeting_name,

+           description: m.meeting_information,

+           time: mTime.toDate(),

+           timeEnd: mTimeEnd.toDate(),

+           displayTime: {

+             // Format momentjs object to the defined format for display

+             dateString: mTime.format(DATE_FORMAT),

+             timeString: mTime.format(TIME_FORMAT)

+           },

+           displayTimeEnd: {

+             dateString: mTimeEnd.format(DATE_FORMAT),

+             timeString: mTimeEnd.format(TIME_FORMAT),

+           },

+           location: m.meeting_location,

+         };

+       }));

    }

  }

  

- 

- function dateToMoment(date, time, timezone) {

-   return moment.tz(date + 'T' + time + 'Z', timezone).tz('Etc/UTC');

+ /**

+  * Convert a date consisting of date, time and timezone as different strings to

+  * a single momentjs date

+  *

+  * @param date     Date string

+  * @param time     Time string

+  * @param timezone Timezone identifier

+  */

+ function dateToMoment(date:string, time:string, timezone:string) {

+   const m = moment.tz(`${date} ${time}`, timezone).tz('UTC');

+   return m;

  }

-  

+ 

@@ -1,84 +0,0 @@ 

- import { Injectable } from '@angular/core';

- import 'rxjs/add/operator/map';

- 

- import * as _ from 'lodash';

- 

- import { Request } from '../request/request';

- 

- 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:Request) {

-     this.posts = [];

-     this.media_url = [];

-   }

- 

-   getPosts() {

-     if (!_.isEmpty(this.posts)) {

-       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 resolve(this.posts);

-         }).catch(reject);

-     });

-   }

- /*  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);

-     });

-   }*/

- }

@@ -0,0 +1,80 @@ 

+ import 'rxjs/add/operator/map';

+ import { Injectable } from '@angular/core';

+ 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');

+ 

+ /**

+  * 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,

+ 

+   /**

+    * 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,

+ 

+   /**

+    * Time of publication

+    */

+   publishedAt: Date

+ }

+ 

+ /**

+  * 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,

+         publishedAt: new Date(post.date_gmt+'Z'),

+       })));

+   }

+ }

@@ -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];

-   }

- }

@@ -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

-         );

-     });

-   }

- }

- 

@@ -0,0 +1,70 @@ 

+ 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, FACEBOOK } from './social';

+ import { Observable } from 'rxjs/Observable';

+ import { Observer } from 'rxjs/Observer';

+ 

+ /**

+  * 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);

+   }

+ 

+   /**

+    * 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) {

+           emitter.error(res.error);

+         } else {

+           emitter.next(res);

+         }

+         emitter.complete();

+       });

+     });

+   }

+ 

+   /**

+    * 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 => {

+         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;

+       });

+   }

+ }

@@ -0,0 +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,

+ 

+   /**

+    * Text content of the post

+    */

+   content:string,

+ }

+ 

+ /**

+  * A generic social media provider

+  */

+ export interface SocialProvider {

+ 

+   /**

+    * 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[]>

+ }

@@ -0,0 +1,39 @@ 

+ import 'rxjs/add/operator/map';

+ import 'rxjs/add/operator/do';

+ import { Injectable } from '@angular/core';

+ import { SocialProvider, Post } from './social';

+ import { HttpClient } from '@angular/common/http';

+ import { chooseEndpoint } from '../../utils';

+ import { Observable } from 'rxjs/Observable';

+ import ENV from '@environment';

+ 

+ const ENDPOINT = chooseEndpoint('/twitter', 'https://api.twitter.com/1.1');

+ 

+ /**

+  * 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 },

+       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',

+     })));

+   }

+ }

+ 

file removed
-47
@@ -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);

-     });

-   }

- }

- 

file added
+11
@@ -0,0 +1,11 @@ 

+ 

+ /**

+  * 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;

+ }

This PR contains code that is in #55 , this needs to be rebased before it can be merged.

Rebased. Good to go!

1 new commit added

  • Use Observables instead of Promises to match with Angular API
7 years ago

rebased onto ffcbf838f0469a328fcbcb542c1d605bd94d1ab6

7 years ago

3 new commits added

  • Fix TS lint errors
  • Use Observables instead of Promises to match with Angular API
  • Add type declarations to Http and Social providers
7 years ago

1 new commit added

  • Add documentation to providers and pages
7 years ago

Shouldnt it be containing all the pages ?

@amitosh Ionic serve is throwing following error :-

[10:40:19]  typescript: src/app/main.ts, line: 2 
            Cannot find module '@environment'. 

       L1:  import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
       L2:  import ENV from '@environment';
       L3:  import { AppModule } from './app.module';

[10:40:19]  typescript: src/providers/social/facebook.ts, line: 3 
            Cannot find module '@environment'. 

       L2:  import 'rxjs/add/operator/map';
       L3:  import ENV from '@environment';
       L4:  import { Facebook } from 'fb';

[10:40:19]  typescript: src/providers/social/twitter.ts, line: 8 
            Cannot find module '@environment'. 

       L7:  import { Observable } from 'rxjs/Observable';
       L8:  import ENV from '@environment';

@a2batic The tree shows all pages are added: https://pagure.io/fork/amitosh/Fedora-app/blob/tsify/f/src/app/app.component.ts

There must be some error in rendering the diff at Pagure

LGTM, @amitosh, please rebase the PR.

rebased onto 824f8c3

7 years ago

Pull-Request has been merged by a2batic

7 years ago