diff --git a/package.json b/package.json
index 5e1de06..51ca0d1 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,10 @@
"@angular/platform-browser-dynamic": "2.2.1",
"@angular/platform-server": "2.2.1",
"@ionic/storage": "1.1.8",
+ "apollo-angular": "^0.11.0",
+ "apollo-client": "^0.8.7",
+ "apollo-client-rxjs": "^0.5.1",
+ "graphql-tag": "^1.2.4",
"ionic-angular": "2.0.1",
"ionic-native": "2.5.1",
"ionicons": "3.0.0",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 5d514e4..a7af541 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,10 +1,24 @@
+import { AppConfig } from './app.config';
+import { ApolloClient, createNetworkInterface } from 'apollo-client';
+import { ApolloModule } from 'apollo-angular';
import { BookingPage } from '../pages/booking/booking';
-import { BookingService } from '../services/booking.service';
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
+
+// by default, this client will send queries to `/graphql` (relative to the URL of your app)
+const client = new ApolloClient({
+ networkInterface: createNetworkInterface({
+ uri: AppConfig.apiEndpoint
+ }),
+});
+
+export function provideClient(): ApolloClient {
+ return client;
+}
+
@NgModule({
declarations: [
MyApp,
@@ -12,7 +26,8 @@ import { HomePage } from '../pages/home/home';
BookingPage
],
imports: [
- IonicModule.forRoot(MyApp)
+ IonicModule.forRoot(MyApp),
+ ApolloModule.forRoot(provideClient)
],
bootstrap: [IonicApp],
entryComponents: [
@@ -23,9 +38,8 @@ import { HomePage } from '../pages/home/home';
providers: [
{
provide: ErrorHandler,
- useClass: IonicErrorHandler
+ useClass: IonicErrorHandler,
},
- BookingService
]
})
export class AppModule { }
diff --git a/src/pages/home/home.html b/src/pages/home/home.html
index 2b0c768..6697fd3 100644
--- a/src/pages/home/home.html
+++ b/src/pages/home/home.html
@@ -25,11 +25,11 @@
-
+ refreshingText="Update in progress">
diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts
index 904f285..983abb4 100644
--- a/src/pages/home/home.ts
+++ b/src/pages/home/home.ts
@@ -1,10 +1,32 @@
+import { Subject, Subscription } from 'rxjs/Rx';
+import { Apollo, ApolloQueryObservable } from 'apollo-angular';
import { Room } from '../../models/room';
-import { BookingService } from '../../services/booking.service';
import { Component, ViewChild } from '@angular/core';
import { Content, NavController, AlertController } from 'ionic-angular';
import { BookingPage } from '../booking/booking';
import { Vibration, BarcodeScanner } from 'ionic-native';
+import gql from 'graphql-tag';
+
+const floorMasterRoomNumberQuery = gql`
+ query AvailableRoomsQuery($roomNumber: Int!)
+ { roomsOnFloor: rooms(floorMasterRoomNumber: $roomNumber) {
+ name
+ number
+ capacity
+ availability {
+ busy
+ availableFor
+ availableFrom
+ }
+ }
+ }`
+ ;
+
+interface QueryResponse {
+ roomsOnFloor: Room[];
+}
+
@Component({
selector: 'page-home',
templateUrl: 'home.html'
@@ -13,43 +35,57 @@ import { Vibration, BarcodeScanner } from 'ionic-native';
export class HomePage {
@ViewChild(Content) content: Content;
+ roomsObs: ApolloQueryObservable;
+ rooms: Room[] = [];
+ filteredRooms: Room[] = [];
+ availabilityFilter: string;
+ floorMasterRoomNumber: Subject = new Subject();
+ updateInProgress: boolean;
+ roomSelected: number;
+ roomsSubscription: Subscription;
- private rooms: Room[];
- public filteredRooms: Room[];
- private availabilityFilter: string;
- private roomSelected: number;
- private updateInProgress: boolean;
+ constructor(
+ public navCtrl: NavController,
+ private apollo: Apollo,
+ private alertCtrl: AlertController
+ ){}
- constructor(public navCtrl: NavController, private bookingService: BookingService, private alertCtrl: AlertController) {
- this.roomSelected = 412; // "magic" room number
- this.updateRooms();
- }
-
- updateRooms() {
+ //Ionic's alternative to ngOnInit()
+ ionViewWillEnter() {
+ this.roomSelected = 412; //Magic room number - to be substituted with config
this.updateInProgress = true;
- this.filteredRooms = null; //toggles "loading" spinner on UI
- this.bookingService.getMeetingRooms(this.roomSelected).subscribe(
- result => this.processData(result),
- err => {
- this.showError(err);
- }
+
+ this.roomsObs = this.apollo.watchQuery({
+ query: floorMasterRoomNumberQuery,
+ variables: {roomNumber: this.roomSelected}
+ });
+
+ this.roomsSubscription = this.roomsObs.subscribe(
+ res => {
+ this.processData(res)
+ this.updateInProgress = false;
+ },
+ err => this.rooms = []
);
}
+
+ //Ionic's alternative to ngOnInit()
+ ionViewDidLeave() {
+ this.roomsSubscription.unsubscribe();
+ }
- processData(result: Room[]) {
- this.rooms = result;
+ //Update rooms list, apply default filter, drop "sync in progress"
+ processData(res, refresher?) {
+ this.rooms = res.data.roomsOnFloor || [];
this.availabilityFilter = 'available';
+ this.updateInProgress = false;
this.showAvailableRooms();
- //Vibration added to check integration with native functionality
- try {
- Vibration.vibrate(1000)
+ if (refresher !== undefined) {
+ refresher.complete();
}
- catch(e) {
- console.log('Vibration failed: '+ (e).message);
- };
- this.updateInProgress = false;
}
-
+
+ //Alert modal window
showError(err) {
let alert = this.alertCtrl.create({
title: 'Error',
@@ -70,13 +106,23 @@ export class HomePage {
this.content.scrollToTop();
}
+
+ //Refresh spinner
doRefresh(refresher) {
- this.updateRooms();
- refresher.complete();
+ this.rooms = [];
+ this.filteredRooms = [];
+ this.roomsObs.refetch()
+ .then(res => {
+ this.processData(res, refresher);
+ })
+ .catch(err => {
+ this.showError(err);
+ refresher.complete()
+ });
}
-
+
openBookingPage(room: Room) {
- this.navCtrl.push(BookingPage, {room});
+ this.navCtrl.push(BookingPage, { room });
}
// Barcode scanning using native cordova plugin
@@ -88,12 +134,26 @@ export class HomePage {
let roomNumber = Number(url.substr(28, 3));
if (roomNumber > 0) {
this.roomSelected = roomNumber;
- this.updateRooms();
+ this.roomsObs.refetch({roomNumber: roomNumber});
} else {
this.showError('Could not identify room number');
}
}
- },
- err => this.showError('Unknown error')
- )}
+ },
+ err => {
+ this.showError('Unknown error');
+ }
+ )
+ }
+
+ //Vibration added to check integration with native functionality
+ vibrate() {
+ try {
+ Vibration.vibrate(1000)
+ }
+ catch (e) {
+ console.log('Vibration failed: ' + (e).message);
+ };
+ }
+
}
\ No newline at end of file
diff --git a/src/services/booking.service.ts b/src/services/booking.service.ts
deleted file mode 100644
index 665c34a..0000000
--- a/src/services/booking.service.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs/Observable';
-import { Http, URLSearchParams, Response } from '@angular/http';
-import { AppConfig } from '../app/app.config';
-import { Room } from '../models/room';
-
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/map';
-import 'rxjs/add/observable/throw';
-
-//Implemented for POC purpose. Selected room will not be displayed in results. To be changed to something reasonable.
-const floorMasterRoomNumberQuery = 'query AvailableRoomsQuery($roomNumber: Int!) { roomsOnFloor: rooms(floorMasterRoomNumber: $roomNumber) { name number capacity availability { busy availableFor availableFrom __typename } __typename }}';
-
-@Injectable()
-export class BookingService {
-
- constructor(private http: Http) {
- }
-
- getMeetingRooms(roomSelected: number): Observable {
- let params: URLSearchParams = new URLSearchParams();
- let variables = {"roomNumber": roomSelected};
- params.set('query', floorMasterRoomNumberQuery);
- params.set('variables', JSON.stringify(variables));
- return this.http.get(AppConfig.apiEndpoint, { search: params })
- .map(this.extractData)
- .catch(this.handleError);;
- }
-
- private extractData(res: Response) {
- let body = res.json();
- return body.data.roomsOnFloor || {};
- }
-
- private handleError(error: Response | any) {
- // In a real world app, we might use a remote logging infrastructure
- let errMsg: string;
- if (error instanceof Response) {
- const body = error.json() || '';
- const err = body.error || JSON.stringify(body);
- errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
- } else {
- errMsg = error.message ? error.message : error.toString();
- }
- console.error(errMsg);
- return Observable.throw('connection error - '+ errMsg);
- }
-
-}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 8d7f092..c4d583f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -97,6 +97,18 @@
localforage "~1.4.2"
localforage-cordovasqlitedriver "~1.5.0"
+"@types/async@^2.0.31":
+ version "2.0.38"
+ resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.38.tgz#5c369dcb14788da0621daafa8594a053b0edcb21"
+
+"@types/graphql@^0.8.0":
+ version "0.8.6"
+ resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.8.6.tgz#b34fb880493ba835b0c067024ee70130d6f9bb68"
+
+"@types/isomorphic-fetch@0.0.30":
+ version "0.0.30"
+ resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.30.tgz#a21717624cde9a48c2db53a4e500fc5c32a99bbc"
+
"@types/localforage@0.0.30":
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.30.tgz#3d60a6bf6dda38e3f8a469611598379f1f649509"
@@ -164,6 +176,30 @@ anymatch@^1.3.0:
arrify "^1.0.0"
micromatch "^2.1.5"
+apollo-angular@^0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/apollo-angular/-/apollo-angular-0.11.0.tgz#57da1f5bc3472c18d8c061ba544c2d1e0ac0f405"
+ dependencies:
+ apollo-client-rxjs "~0.5.0"
+
+apollo-client-rxjs@^0.5.1, apollo-client-rxjs@~0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/apollo-client-rxjs/-/apollo-client-rxjs-0.5.1.tgz#52cd0e836f28178d5f2cbdee9917b3395afd063f"
+
+apollo-client@^0.8.7:
+ version "0.8.7"
+ resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-0.8.7.tgz#73f52d1dd6dfe0f07e8bfe05a8e47c4bcd3bf7a3"
+ dependencies:
+ graphql-anywhere "^2.1.0"
+ graphql-tag "^1.1.1"
+ redux "^3.4.0"
+ symbol-observable "^1.0.2"
+ whatwg-fetch "^2.0.0"
+ optionalDependencies:
+ "@types/async" "^2.0.31"
+ "@types/graphql" "^0.8.0"
+ "@types/isomorphic-fetch" "0.0.30"
+
aproba@^1.0.3:
version "1.1.1"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab"
@@ -1456,6 +1492,14 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
+graphql-anywhere@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-2.2.0.tgz#652c3fa23a4a6cfeb98817512fb48100b97f3d5c"
+
+graphql-tag@^1.1.1, graphql-tag@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-1.2.4.tgz#90c59bea41378513fd7213dc92537fcd20e4570f"
+
har-validator@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
@@ -1861,6 +1905,10 @@ localforage@>=1.4.0, localforage@~1.4.2:
dependencies:
lie "3.0.2"
+lodash-es@^4.2.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
+
lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
@@ -1881,7 +1929,7 @@ lodash.some@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
-lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0:
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.2.1:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -1893,7 +1941,7 @@ longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
-loose-envify@^1.0.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -2536,6 +2584,15 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
+redux@^3.4.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d"
+ dependencies:
+ lodash "^4.2.1"
+ lodash-es "^4.2.1"
+ loose-envify "^1.1.0"
+ symbol-observable "^1.0.2"
+
reflect-metadata@^0.1.2:
version "0.1.9"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.9.tgz#987238dc87a516895fe457f130435ffbd763a4d4"
@@ -2945,7 +3002,7 @@ sw-toolbox@3.4.0:
path-to-regexp "^1.0.1"
serviceworker-cache-polyfill "^4.0.0"
-symbol-observable@^1.0.1:
+symbol-observable@^1.0.1, symbol-observable@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
@@ -3236,6 +3293,10 @@ websocket-extensions@>=0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
+whatwg-fetch@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e"
+
which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"