diff --git a/package.json b/package.json
index 0241efa..472fe66 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,10 @@
"@angular/cli": "1.6.3",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
+ "@types/gapi": "^0.0.35",
+ "@types/gapi.auth2": "^0.0.46",
+ "@types/gapi.client": "^1.0.0",
+ "@types/gapi.client.sheets": "^4.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index d425c6f..dc8c2d1 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,7 +1,16 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
-const routes: Routes = [];
+import { HomeComponent } from './home/home.component';
+
+import { AuthGuard } from './auth.guard';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: HomeComponent
+ }
+];
@NgModule({
imports: [RouterModule.forRoot(routes)],
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 1094e7e..0d4a7b9 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,21 +1,4 @@
-
-
-
- Welcome to {{ title }}!
-
-

+
+
+
-
Here are some links to help you start:
-
-
-
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 2c5e1ac..4a7ba58 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -4,17 +4,22 @@ import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
+import { HeaderComponent } from './header/header.component';
+import { HomeComponent } from './home/home.component';
+import { GApiService } from './gapi.service';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ HeaderComponent,
+ HomeComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
- providers: [],
+ providers: [GApiService],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/src/app/auth.guard.spec.ts b/src/app/auth.guard.spec.ts
new file mode 100644
index 0000000..7ed05ee
--- /dev/null
+++ b/src/app/auth.guard.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, async, inject } from '@angular/core/testing';
+
+import { AuthGuard } from './auth.guard';
+
+describe('AuthGuard', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [AuthGuard]
+ });
+ });
+
+ it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
+ expect(guard).toBeTruthy();
+ }));
+});
diff --git a/src/app/auth.guard.ts b/src/app/auth.guard.ts
new file mode 100644
index 0000000..d05535a
--- /dev/null
+++ b/src/app/auth.guard.ts
@@ -0,0 +1,19 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+import { GApiService } from './gapi.service';
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+ constructor (private gapiService: GApiService, private router: Router) { }
+
+ canActivate (
+ next: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot): Observable
| Promise | boolean {
+ const authenticated = this.gapiService.isAuthenticated();
+ if (!authenticated) {
+ this.router.navigate(['/']);
+ }
+ return authenticated;
+ }
+}
diff --git a/src/app/gapi.service.spec.ts b/src/app/gapi.service.spec.ts
new file mode 100644
index 0000000..ab3cca8
--- /dev/null
+++ b/src/app/gapi.service.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { GApiService } from './gapi.service';
+
+describe('GapiService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [GApiService]
+ });
+ });
+
+ it('should be created', inject([GApiService], (service: GApiService) => {
+ expect(service).toBeTruthy();
+ }));
+});
diff --git a/src/app/gapi.service.ts b/src/app/gapi.service.ts
new file mode 100644
index 0000000..3e7a814
--- /dev/null
+++ b/src/app/gapi.service.ts
@@ -0,0 +1,84 @@
+import { bindCallback } from 'rxjs/Observable/bindCallback';
+import { fromPromise } from 'rxjs/Observable/fromPromise';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+
+const API_KEY = 'AIzaSyA9BX0C-ku3NBkTSeHY59DiJi02U-DbCe0';
+const CLIENT_ID = '505888163689-q01h4puni0e74t36m04mcksr0g7pti5v.apps.googleusercontent.com';
+
+const DISCOVERY_DOCS: string[] = ['https://sheets.googleapis.com/$discovery/rest?version=v4'];
+const SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly';
+
+function gapiRequestToObservable(request: gapi.client.Request): Observable {
+ return fromPromise>(request).map(res => res.result);
+}
+
+/**
+ * Takes a positive integer and returns the corresponding column name.
+ * @param {number} num The positive integer to convert to a column name.
+ * @return {string} The column name.
+ */
+function numberToColumnName(num: number): string {
+ let ret = '';
+ for (; num > 0; num = Math.floor(num % 26)) {
+ num--; // because nobody starts counting columns at 0 and A needs to be 1
+ ret = String.fromCharCode(Math.floor(num % 26) + 65) + ret;
+ }
+ return ret;
+}
+
+@Injectable()
+export class GApiService {
+ private observableGetAuthInstance: () => Observable;
+ private authInstance: gapi.auth2.GoogleAuth;
+
+ constructor () {
+ gapi.load('client:auth2', () => {
+ gapi.client.init({
+ apiKey: API_KEY,
+ clientId: CLIENT_ID,
+ discoveryDocs: DISCOVERY_DOCS,
+ scope: SCOPES
+ }).then(() => {
+ this.authInstance = gapi.auth2.getAuthInstance();
+ // Listen for sign-in state changes.
+ this.observableGetAuthInstance = bindCallback(this.authInstance.isSignedIn.listen);
+ });
+ });
+ }
+
+ get authStatusChange(): Observable { return this.observableGetAuthInstance(); }
+
+ isAuthenticated (): boolean {
+ return this.authInstance.isSignedIn.get();
+ }
+
+ signIn (): void {
+ gapi.auth2.getAuthInstance().signIn();
+ }
+
+ signOut (): void {
+ gapi.auth2.getAuthInstance().signOut();
+ }
+
+ getSpreadsheet (id: string): Observable {
+ return gapiRequestToObservable(gapi.client.spreadsheets.get({
+ spreadsheetId: id
+ }));
+ }
+
+ getDataOnSheet (spreadsheetId: string, sheet: gapi.client.sheets.Sheet) {
+ gapi.client.spreadsheets.values.get({
+ spreadsheetId,
+ range: sheet.properties.title + '!A1:1',
+ majorDimension: 'ROWS'
+ }).then(firstRowResponse => {
+ const firstRow = firstRowResponse.result.values[0];
+ gapi.client.spreadsheets.values.get({
+ spreadsheetId,
+ range: sheet.properties.title + '!A1:' + numberToColumnName(firstRow.length)
+ });
+ });
+ }
+}
diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
new file mode 100644
index 0000000..d7db7ba
--- /dev/null
+++ b/src/app/header/header.component.html
@@ -0,0 +1,11 @@
+
diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts
new file mode 100644
index 0000000..2d0479d
--- /dev/null
+++ b/src/app/header/header.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HeaderComponent } from './header.component';
+
+describe('HeaderComponent', () => {
+ let component: HeaderComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ HeaderComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HeaderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts
new file mode 100644
index 0000000..137e18b
--- /dev/null
+++ b/src/app/header/header.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { GApiService } from '../gapi.service';
+import { Subscription } from 'rxjs/Subscription';
+
+@Component({
+ selector: 'app-header',
+ templateUrl: './header.component.html',
+ styleUrls: ['./header.component.css']
+})
+export class HeaderComponent implements OnInit, OnDestroy {
+ private sub: Subscription;
+ constructor(private gapiService: GApiService) {
+ }
+
+ loggedIn = false;
+
+ ngOnInit(): void {
+ this.loggedIn = this.gapiService.isAuthenticated();
+ this.sub = this.gapiService.authStatusChange.subscribe(newValue => this.loggedIn = newValue);
+ }
+
+ ngOnDestroy(): void {
+ this.sub.unsubscribe();
+ }
+}
diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html
new file mode 100644
index 0000000..afc16a3
--- /dev/null
+++ b/src/app/home/home.component.html
@@ -0,0 +1,3 @@
+
+ home works!
+
diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts
new file mode 100644
index 0000000..490e81b
--- /dev/null
+++ b/src/app/home/home.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HomeComponent } from './home.component';
+
+describe('HomeComponent', () => {
+ let component: HomeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ HomeComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(HomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts
new file mode 100644
index 0000000..33fd770
--- /dev/null
+++ b/src/app/home/home.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-home',
+ templateUrl: './home.component.html',
+ styleUrls: ['./home.component.css']
+})
+export class HomeComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/index.html b/src/index.html
index d148cc4..0cb7b47 100644
--- a/src/index.html
+++ b/src/index.html
@@ -5,8 +5,18 @@
QContacts
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tslint.json b/tslint.json
index a2e30ef..2852bfa 100644
--- a/tslint.json
+++ b/tslint.json
@@ -24,7 +24,8 @@
"import-spacing": true,
"indent": [
true,
- "spaces"
+ "spaces",
+ 2
],
"interface-over-type-literal": true,
"label-position": true,
diff --git a/yarn.lock b/yarn.lock
index 07b8aa5..615a61c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -185,6 +185,26 @@
version "0.0.11"
resolved "https://registry.yarnpkg.com/@schematics/schematics/-/schematics-0.0.11.tgz#c8f70f270ed38f29b2873248126fd59abd635862"
+"@types/gapi.auth2@^0.0.46":
+ version "0.0.46"
+ resolved "https://registry.yarnpkg.com/@types/gapi.auth2/-/gapi.auth2-0.0.46.tgz#5c929d3e66d74b84293c65aaa0ce12e0f8d4fb36"
+ dependencies:
+ "@types/gapi" "*"
+
+"@types/gapi.client.sheets@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@types/gapi.client.sheets/-/gapi.client.sheets-4.0.0.tgz#bfc4f4f24a0292da3b4d7b561d6d67ded7b11ebb"
+ dependencies:
+ "@types/gapi.client" "*"
+
+"@types/gapi.client@*", "@types/gapi.client@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@types/gapi.client/-/gapi.client-1.0.0.tgz#aa42501465fcf6440421050da70ec724d4c4c4df"
+
+"@types/gapi@*", "@types/gapi@^0.0.35":
+ version "0.0.35"
+ resolved "https://registry.yarnpkg.com/@types/gapi/-/gapi-0.0.35.tgz#8e0d672d66d8c6ce8493f5e8e550344f7ff500dc"
+
"@types/jasmine@*":
version "2.8.3"
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.3.tgz#f910edc67d69393d562d10f8f3d205ea3f3306bf"