diff --git a/Ionic2/package.json b/Ionic2/package.json index ef7f9c2..0bb6531 100644 --- a/Ionic2/package.json +++ b/Ionic2/package.json @@ -16,7 +16,8 @@ "ionic-angular": "^2.0.0-rc.0", "ionicons": "^3.0.0", "@ionic/storage": "^1.0.3", - "ionic-native": "^2.0.3" + "ionic-native": "^2.0.3", + "angular2-jwt": "^0.1.21" }, "devDependencies": { "@ionic/app-scripts": "^0.0.23", diff --git a/Ionic2/src/app/app.component.ts b/Ionic2/src/app/app.component.ts index de096f5..20f33b0 100644 --- a/Ionic2/src/app/app.component.ts +++ b/Ionic2/src/app/app.component.ts @@ -1,21 +1,79 @@ -import { Component } from '@angular/core'; -import { Platform } from 'ionic-angular'; +import { Component, ViewChild, provide } from '@angular/core'; +import { Platform, MenuController, Nav } from 'ionic-angular'; import { StatusBar } from 'ionic-native'; -import { TabsPage } from '../pages/tabs/tabs'; +import {Page1} from '../pages/page1/page1'; +import {Page3} from '../pages/page3/page3'; +import {LoginPage} from '../pages/loginPage/loginPage'; +import {ProfilePage} from '../pages/profilePage/profilePage'; +import {ContactsPage} from '../pages/contactsPage/contactsPage'; +import {PendingInvitesPage} from '../pages/pendingInvitesPage/pendingInvitesPage'; +import {SettingsPage} from '../pages/settingsPage/settingsPage'; +import {DiscoverUsersPage} from '../pages/discoverUsersPage/discoverUsersPage'; +import {ContactsService} from '../services/contacts.service'; +import {StorageService} from '../services/storage.service'; +import {PreferencesService} from '../services/preferences.service'; +import {UserInfoService} from '../services/userInfo.service'; + +import {Http} from '@angular/http'; +import {AuthHttp, AuthConfig} from 'angular2-jwt'; +import {AuthService} from '../services/auth.service'; +import {Secret} from '../secrets/secret'; + @Component({ - template: `` + templateUrl: `app.html` }) export class MyApp { - rootPage = TabsPage; + @ViewChild('nav') nav: Nav; + public primaryPages:any[]; + public settingsPages:any[]; + public rootPage:any; + + constructor(public platform: Platform, public menu: MenuController, + public preferencesService: PreferencesService, + public auth: AuthService, public userInfoService: UserInfoService) { + + this.initializeApp(); + + // set our app's pages (they appear in menu) + this.primaryPages = [ + { title: 'My Profile', component: ProfilePage, icon: 'person' }, + { title: 'My Contacts', component: ContactsPage, icon: 'contacts' }, + { title: 'Pending Invites', component: PendingInvitesPage, icon: 'person-add' }, + { title: 'Discover Users', component: DiscoverUsersPage, icon: 'people' } + ]; + + this.settingsPages = [ + { title: 'Settings', component: SettingsPage, icon: 'settings' } + ]; - constructor(platform: Platform) { - platform.ready().then(() => { + this.rootPage = LoginPage; + } + + initializeApp() { + this.platform.ready().then(() => { // Okay, so the platform is ready and our plugins are available. // Here you can do any higher level native things you might need. StatusBar.styleDefault(); + this.userInfoService.initialize(); + this.preferencesService.initializePreferences(); + this.auth.startupTokenRefresh(); }); } + + openPage(page) { + // close the menu when clicking a link from the menu + this.menu.close(); + // navigate to the new page if it is not the current page + //let nav = this.app.getComponent('nav'); + this.nav.setRoot(page.component); + } + + sendFeedback(){ + window.open( + 'mailto:' + Secret.FEEDBACK_EMAIL + + '?subject=' + Secret.FEEDBACK_SUBJECT, '_system'); + } } diff --git a/Ionic2/src/app/app.html b/Ionic2/src/app/app.html new file mode 100644 index 0000000..125126f --- /dev/null +++ b/Ionic2/src/app/app.html @@ -0,0 +1,24 @@ + + + Menu + + + + + + + + + + + + diff --git a/Ionic2/src/app/app.module.ts b/Ionic2/src/app/app.module.ts index 36bcff0..9c1f168 100644 --- a/Ionic2/src/app/app.module.ts +++ b/Ionic2/src/app/app.module.ts @@ -1,30 +1,83 @@ -import { NgModule } from '@angular/core'; +import { NgModule, provide } from '@angular/core'; import { IonicApp, IonicModule } from 'ionic-angular'; import { MyApp } from './app.component'; -import { AboutPage } from '../pages/about/about'; -import { ContactPage } from '../pages/contact/contact'; -import { HomePage } from '../pages/home/home'; -import { TabsPage } from '../pages/tabs/tabs'; + +//pages +import {Page1} from '../pages/page1/page1'; +import {Page3} from '../pages/page3/page3'; +import {LoginPage} from '../pages/loginPage/loginPage'; +import {ProfilePage} from '../pages/profilePage/profilePage'; +import {ContactsPage} from '../pages/contactsPage/contactsPage'; +import {PendingInvitesPage} from '../pages/pendingInvitesPage/pendingInvitesPage'; +import {SettingsPage} from '../pages/settingsPage/settingsPage'; +import {DiscoverUsersPage} from '../pages/discoverUsersPage/discoverUsersPage'; +import {ChatPage} from '../pages/chatPage/chatPage'; +import {ContactPage} from '../pages/contactPage/contactPage'; + +//components +import {ElasticTextarea} from '../components/elasticTextarea'; +import {ProfileHeader} from '../components/profileHeader'; +import {ChatBubble} from '../components/chatBubble/chatBubble'; + +//services (providers) +import {AuthService} from '../services/auth.service'; +import {ContactsService} from '../services/contacts.service'; +import {PreferencesService} from '../services/preferences.service'; +import {StorageService} from '../services/storage.service'; +import {UserInfoService} from '../services/userInfo.service'; + +//external +import {Http} from '@angular/http'; +import {AuthHttp, AuthConfig} from 'angular2-jwt'; @NgModule({ declarations: [ MyApp, - AboutPage, + Page1, + Page3, + LoginPage, + ProfilePage, + ContactsPage, + PendingInvitesPage, + SettingsPage, + DiscoverUsersPage, + ChatPage, ContactPage, - HomePage, - TabsPage + ElasticTextarea, + ProfileHeader, + ChatBubble ], imports: [ - IonicModule.forRoot(MyApp) + IonicModule.forRoot(MyApp, {})//config object here + ], + bootstrap: [ + IonicApp ], - bootstrap: [IonicApp], entryComponents: [ MyApp, - AboutPage, - ContactPage, - HomePage, - TabsPage + Page1, + Page3, + LoginPage, + ProfilePage, + ContactsPage, + PendingInvitesPage, + SettingsPage, + DiscoverUsersPage, + ChatPage, + ContactPage ], - providers: [] + providers: [ + ContactsService, + PreferencesService, + StorageService, + UserInfoService, + provide(AuthHttp, { + useFactory: (http) => { + return new AuthHttp(new AuthConfig({noJwtError: true}), http); + }, + deps: [Http] + }), + AuthService + ] }) export class AppModule {} diff --git a/Ionic2/src/components/chatBubble/chatBubble.scss b/Ionic2/src/components/chatBubble/chatBubble.scss new file mode 100644 index 0000000..a2862a2 --- /dev/null +++ b/Ionic2/src/components/chatBubble/chatBubble.scss @@ -0,0 +1,83 @@ +.chatBubble{ + + //position: relative; + + // :last-child { + // margin-bottom: 10px; + // } + + img.profile-pic { + width: 40px; + height: 40px; + border-radius: 50%; + position: absolute; + bottom: 10px; + } + + img.profile-pic.left { + left: 10px; + } + + img.profile-pic.right { + right: 10px; + } + + .message { + font-size: medium; + word-wrap: break-word; + white-space: pre-wrap; + } + + .message-detail { + white-space: nowrap; + font-size: 14px; + } + + .chat-bubble { + border-radius: 5px; + display: inline-block; + padding: 10px 18px; + position: relative; + margin: 10px; + max-width: 80%; + } + + .chat-bubble:before { + content: "\00a0"; + display: block; + height: 16px; + width: 9px; + position: absolute; + bottom: -7.5px; + } + + .chat-bubble.left { + background-color: map-get($chat-bubble, background-left); + float: left; + margin-left: 45px; + } + + .chat-bubble.left:before { + background-color: map-get($chat-bubble, background-left); + left: 10px; + -webkit-transform: rotate(70deg) skew(5deg); + } + + .chat-bubble.right { + background-color: map-get($chat-bubble, background-right); + color: #000; + float: right; + margin-right: 45px; + } + + .chat-bubble.right:before { + background-color: map-get($chat-bubble, background-right); + right: 10px; + -webkit-transform: rotate(118deg) skew(-5deg); + } + + .chat-bubble.right a.autolinker { + color: #000; + font-weight: bold; + } +} diff --git a/Ionic2/src/components/chatBubble/chatBubble.ts b/Ionic2/src/components/chatBubble/chatBubble.ts new file mode 100644 index 0000000..e0ef369 --- /dev/null +++ b/Ionic2/src/components/chatBubble/chatBubble.ts @@ -0,0 +1,31 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'chat-bubble', + inputs: ['msg: message'], + template: + ` +
+ +
+
{{msg.content}}
+
+ {{msg.senderName}} , + {{msg.time}} +
+
+
+ ` +}) +export class ChatBubble { + public msg:any; + + constructor() { + this.msg = { + content : 'Am I dreaming?', + position : 'left', + time : '12/3/2016', + senderName : 'Gregory' + } + } +} diff --git a/Ionic2/src/components/elasticTextarea.ts b/Ionic2/src/components/elasticTextarea.ts new file mode 100644 index 0000000..b74348a --- /dev/null +++ b/Ionic2/src/components/elasticTextarea.ts @@ -0,0 +1,44 @@ +import {Component, ViewChild} from '@angular/core'; + +@Component({ + selector: 'elastic-textarea', + inputs: ['placeholder', 'lineHeight'], + template: + ` + + ` +}) +export class ElasticTextarea { + @ViewChild('ionTxtArea') ionTxtArea:any; + public txtArea:any; + public content:string; + public lineHeight:string; + + constructor() { + this.content = ""; + this.lineHeight = "22px"; + } + + public ngAfterViewInit(){ + this.txtArea = this.ionTxtArea._elementRef.nativeElement.children[0]; + this.txtArea.style.height = this.lineHeight + "px"; + this.txtArea.style.resize = 'none'; + } + + public onChange(newValue){ + this.txtArea.style.height = this.lineHeight + "px"; + this.txtArea.style.height = this.txtArea.scrollHeight + "px"; + } + + public clearInput(){ + this.content = ""; + this.txtArea.style.height = this.lineHeight + "px"; + } + + public setFocus(){ + this.ionTxtArea.setFocus() + } +} diff --git a/Ionic2/src/components/profileHeader.ts b/Ionic2/src/components/profileHeader.ts new file mode 100644 index 0000000..aa0f024 --- /dev/null +++ b/Ionic2/src/components/profileHeader.ts @@ -0,0 +1,26 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'profile-header', + inputs: ['fullName', 'profileImage'], + template: + ` +
+ + {{fullName}} +
+ ` +}) +export class ProfileHeader { + public fullName:string; + public profileImage:string; + + constructor() { + this.fullName = 'Dr. House'; + this.profileImage = 'build/img/hugh.png'; + } + + public setFullName(fullName){ + this.fullName = fullName; + } +} diff --git a/Ionic2/src/index.html b/Ionic2/src/index.html index 348c2c4..4e8413a 100644 --- a/Ionic2/src/index.html +++ b/Ionic2/src/index.html @@ -22,6 +22,11 @@ + + + + + diff --git a/Ionic2/src/models/contactModel.ts b/Ionic2/src/models/contactModel.ts new file mode 100644 index 0000000..767f983 --- /dev/null +++ b/Ionic2/src/models/contactModel.ts @@ -0,0 +1,27 @@ +export class ContactModel { + + public firstName:string; + public lastName:string; + public employment:string; + public education:string; + public knowledgeableIn:string; + public interests:string; + public currentGoals:string; + public profileImage:string; + + constructor(firstName, lastName, employment, education){ + + this.firstName = firstName; + this.lastName = lastName; + this.employment = employment || ''; + this.education = education || ''; + this.knowledgeableIn = ''; + this.interests = ''; + this.currentGoals = ''; + this.profileImage = ''; + } + + public getFullName(){ + return this.firstName + ' ' + this.lastName; + } +} diff --git a/Ionic2/src/pages/chatPage/chatPage.html b/Ionic2/src/pages/chatPage/chatPage.html new file mode 100644 index 0000000..f99e8e6 --- /dev/null +++ b/Ionic2/src/pages/chatPage/chatPage.html @@ -0,0 +1,35 @@ + + + + {{contactName}} + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + +
+
diff --git a/Ionic2/src/pages/chatPage/chatPage.scss b/Ionic2/src/pages/chatPage/chatPage.scss new file mode 100644 index 0000000..0c53535 --- /dev/null +++ b/Ionic2/src/pages/chatPage/chatPage.scss @@ -0,0 +1,12 @@ +page-chat { + //because chat-bubbles are wrapped inside ion-label when ion-item is rendered + ion-label { + margin: 0px 8px 13px 0; + } +} + +.chatPageFooter { + .toolbar-background { + background: map-get($colors, background-color); + } +} diff --git a/Ionic2/src/pages/chatPage/chatPage.ts b/Ionic2/src/pages/chatPage/chatPage.ts new file mode 100644 index 0000000..987ce2c --- /dev/null +++ b/Ionic2/src/pages/chatPage/chatPage.ts @@ -0,0 +1,110 @@ +import {Component, ViewChild} from '@angular/core'; +import {NavController, NavParams} from 'ionic-angular'; + +@Component({ + selector: 'page-chat', + templateUrl: 'chatPage.html' +}) +export class ChatPage { + @ViewChild('txtChat') txtChat:any; + @ViewChild('content') content:any; + public messages:any[]; + public contactName:string; + + constructor(public nav: NavController, public navParams: NavParams) { + this.contactName = this.navParams.get('contactName'); + + this.messages = [ + { + img: 'build/img/hugh.png', + position: 'left', + content: 'Hello from the other side.', + senderName: 'Gregory', + time: '28-Jun-2016 21:53' + }, + { + img: 'build/img/hugh.png', + position: 'right', + content: 'Hi! How are?', + senderName: 'Me', + time: '28-Jun-2016 21:55' + }, + { + img: 'build/img/hugh.png', + position: 'left', + content: "This is some really long test that I'm writing here. Let's see how it wraps.", + senderName: 'Gregory', + time: '28-Jun-2016 21:57' + }, + { + img: 'build/img/hugh.png', + position: 'right', + content: 'Hi! How are?', + senderName: 'Me', + time: '28-Jun-2016 21:55' + }, + { + img: 'build/img/hugh.png', + position: 'left', + content: "This is some really long test that I'm writing here. Let's see how it wraps.", + senderName: 'Gregory', + time: '28-Jun-2016 21:57' + }, + { + img: 'build/img/hugh.png', + position: 'right', + content: 'Hi! How are?', + senderName: 'Me', + time: '28-Jun-2016 21:55' + }, + { + img: 'build/img/hugh.png', + position: 'left', + content: "This is some really long test that I'm writing here. Let's see how it wraps.", + senderName: 'Gregory', + time: '28-Jun-2016 21:57' + }, + { + img: 'build/img/hugh.png', + position: 'right', + content: 'Hi! How are?', + senderName: 'Me', + time: '28-Jun-2016 21:55' + }, + { + img: 'build/img/hugh.png', + position: 'left', + content: "This is some really long test that I'm writing here. Let's see how it wraps.", + senderName: 'Gregory', + time: '28-Jun-2016 21:57' + } + ]; + } + + public ionViewDidEnter(){ + this.content.scrollToBottom(300);//300ms animation speed + } + + public sendMessage(){ + this.txtChat.setFocus(); + + this.messages.push({ + img: 'build/img/hugh.png', + position: 'right', + content: this.txtChat.content, + senderName: 'Me', + time: new Date().toLocaleTimeString() + }); + + //console.log(this.txtChat.content); + this.txtChat.clearInput(); + + //without this timeout the list scrolls + //to the second to last element. + //It's some kind of race condition + setTimeout(() => { + this.content.scrollToBottom(300);//300ms animation speed + }); + } + +} diff --git a/Ionic2/src/pages/contactPage/contactPage.html b/Ionic2/src/pages/contactPage/contactPage.html new file mode 100644 index 0000000..78cfec1 --- /dev/null +++ b/Ionic2/src/pages/contactPage/contactPage.html @@ -0,0 +1,42 @@ + + + + + Contact + + + + + + + + + +

{{contact.employment}}

+
+ +

{{contact.education}}

+
+ +

Interests

+

{{contact.interests}}

+
+ +

Knowledgeable In

+

{{contact.knowledgeable}}

+
+ +

Goals

+

{{contact.currentGoals}}

+
+
+ + +
diff --git a/Ionic2/src/pages/contactPage/contactPage.scss b/Ionic2/src/pages/contactPage/contactPage.scss new file mode 100644 index 0000000..9620ca4 --- /dev/null +++ b/Ionic2/src/pages/contactPage/contactPage.scss @@ -0,0 +1,13 @@ +page-contact { + + .section-name { + font-weight: bold; + font-size: medium; + color: map-get($colors, secondary-text); + } + + p { + font-size: large; + color: map-get($colors, primary-text); + } +} diff --git a/Ionic2/src/pages/contactPage/contactPage.ts b/Ionic2/src/pages/contactPage/contactPage.ts new file mode 100644 index 0000000..afc4bc4 --- /dev/null +++ b/Ionic2/src/pages/contactPage/contactPage.ts @@ -0,0 +1,33 @@ +import {Component} from '@angular/core'; +import {ViewChild} from '@angular/core'; +import {NavController, NavParams} from 'ionic-angular'; + +import {ChatPage} from '../chatPage/chatPage'; + + +@Component({ + selector: 'page-contact', + templateUrl: 'contactPage.html' +}) +export class ContactPage { + @ViewChild('header') header:any; + public contact:any; + + constructor(public nav: NavController, public navParams:NavParams) { + this.contact = this.navParams.get('contact'); + + // this.employment = 'Head of Diagnostic @ PPT Hospital'; + // this.education = 'Attended Hopkins University 1979-1984'; + // this.interests = 'Chemistry, Piano , Guitar, Android, Economy, Football'; + // this.knowledgeable = 'Classical Music, Fitness, Movie Trivia, HTML5, Android, JavaScript'; + // this.currentGoals = 'Learn Ionic2, Find a team for basketball'; + } + + public ionViewWillEnter(){ + this.header.setFullName("Dr. Gregory House"); + } + + public openChat(){ + this.nav.push(ChatPage, {contactName: "Dr. Gregory House"}); + } +} diff --git a/Ionic2/src/pages/contactsPage/contactsPage.html b/Ionic2/src/pages/contactsPage/contactsPage.html new file mode 100644 index 0000000..00f5abd --- /dev/null +++ b/Ionic2/src/pages/contactsPage/contactsPage.html @@ -0,0 +1,22 @@ + + + + + My Contacts + + + + + + + + + +

{{cnt.employment}}

+

{{cnt.education}}

+
+
+ +
diff --git a/Ionic2/src/pages/contactsPage/contactsPage.scss b/Ionic2/src/pages/contactsPage/contactsPage.scss new file mode 100644 index 0000000..ddbb0f1 --- /dev/null +++ b/Ionic2/src/pages/contactsPage/contactsPage.scss @@ -0,0 +1,10 @@ +page-contacts { + profile-header{ + div{ + img{ + width: 50px !important; + height: 50px !important; + } + } + } +} diff --git a/Ionic2/src/pages/contactsPage/contactsPage.ts b/Ionic2/src/pages/contactsPage/contactsPage.ts new file mode 100644 index 0000000..690a144 --- /dev/null +++ b/Ionic2/src/pages/contactsPage/contactsPage.ts @@ -0,0 +1,21 @@ +import {Component} from '@angular/core'; +import {NavController} from 'ionic-angular'; +import {ContactsService} from '../../services/contacts.service'; +import {ContactPage} from '../contactPage/contactPage'; + + +@Component({ + selector : 'page-contacts', + templateUrl: 'contactsPage.html' +}) +export class ContactsPage { + public contacts:any[]; + + constructor(public nav:NavController, public contactsService:ContactsService) { + this.contacts = this.contactsService.getContacts(); + } + + public contactSelected(cnt){ + this.nav.push(ContactPage, {contact: cnt}); + } +} diff --git a/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.html b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.html new file mode 100644 index 0000000..8cfd971 --- /dev/null +++ b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.html @@ -0,0 +1,59 @@ + + + + + Discover Users + + + + + + + + + + + +

{{usr.employment}}

+
+ +

{{usr.education}}

+
+ +

Interests

+

{{usr.interests}}

+
+ +

Knowledgeable In

+

{{usr.knowledgeable}}

+
+ +

Goals

+

{{usr.currentGoals}}

+
+
+
+ + No More Users Nearby + +
+ + + +
diff --git a/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.scss b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.scss new file mode 100644 index 0000000..e87519d --- /dev/null +++ b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.scss @@ -0,0 +1,18 @@ +page-discover-users { + .section-name { + font-weight: bold; + font-size: medium; + color: map-get($colors, secondary-text); + } + + p { + font-size: large; + color: map-get($colors, primary-text); + } + + /* prevents the slide content + from being centered in the middle */ + .user-slide .slide-zoom { + height: 100%; + } +} diff --git a/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.ts b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.ts new file mode 100644 index 0000000..d3b53ba --- /dev/null +++ b/Ionic2/src/pages/discoverUsersPage/discoverUsersPage.ts @@ -0,0 +1,87 @@ +import {Component, ViewChild} from '@angular/core'; +import{ + Input, + trigger, + state, + style, + transition, + animate} from '@angular/core'; +import {ContactsService} from '../../services/contacts.service'; + + +@Component({ + selector: 'page-discover-users', + templateUrl: 'discoverUsersPage.html', + animations: [ + trigger('fabState',[ + state('inactive', style({ + transform: 'scale(0)' + })), + state('active', style({ + transform: 'scale(1)' + })), + transition('inactive <=> active', animate('150ms ease-out')) + ]) + ] +}) +export class DiscoverUsersPage { + @ViewChild('slider') slider:any; + public sliderOptions:any; + public users:any[]; + public itemToDelete:number; + public btnState:string; + + constructor(public contactsService: ContactsService) { + this.sliderOptions = { + nested: true, + watchSlidesProgress: true, + loop: false + }; + + this.users = this.contactsService.getNearbyUsers(); + this.itemToDelete = -1; + + this.btnState = 'active'; + } + + public onBtnDismissClicked(){ + let currentIndex = this.slider.getActiveIndex(); + //TODO: update db + this._slideAndRemove(currentIndex) + } + + public onBtnAcceptClicked(){ + let currentIndex = this.slider.getActiveIndex(); + //TODO: update db + this._slideAndRemove(currentIndex) + } + + public onSlideChanged(){ + console.log("onSlideChanged"); + let pendingDeletePosition = this.itemToDelete; + if(pendingDeletePosition !== -1){ + this.itemToDelete = -1; + this.users.splice(pendingDeletePosition, 1); + //slider's slider is a swiper + this.slider.slider.removeSlide(pendingDeletePosition); + this.slider.slider.update(); + } + + if(this.slider.length() === 1 || this.slider.getActiveIndex() + 1 === this.slider.length()){ + console.log('inactive'); + this.btnState= 'inactive'; + }else{ + console.log('active'); + this.btnState= 'active'; + } + } + + public _slideAndRemove(currentIndex){ + //don't remove the last "No More Users Nearby" slide + if(this.slider.length() === 1 || currentIndex + 1 === this.slider.length()) + return; + + this.itemToDelete = currentIndex; + this.slider.slideTo(currentIndex+1, 500); + } +} diff --git a/Ionic2/src/pages/loginPage/loginPage.html b/Ionic2/src/pages/loginPage/loginPage.html new file mode 100644 index 0000000..3293184 --- /dev/null +++ b/Ionic2/src/pages/loginPage/loginPage.html @@ -0,0 +1,19 @@ + + + + Ionic + + + + + + +
+
+

Welcome to Ionic

+

Please wait while we try to sign you in...

+ +
+
+ +
diff --git a/Ionic2/src/pages/loginPage/loginPage.scss b/Ionic2/src/pages/loginPage/loginPage.scss new file mode 100644 index 0000000..881b681 --- /dev/null +++ b/Ionic2/src/pages/loginPage/loginPage.scss @@ -0,0 +1,18 @@ +page-login { + + .Aligner { + display: flex; + align-items: center; + min-height: 100%; + // min-height: 24em; + justify-content: center; + } + + .Aligner-item { + flex: 1; + } + + button { + margin-top: 15px; + } +} diff --git a/Ionic2/src/pages/loginPage/loginPage.ts b/Ionic2/src/pages/loginPage/loginPage.ts new file mode 100644 index 0000000..2b70b02 --- /dev/null +++ b/Ionic2/src/pages/loginPage/loginPage.ts @@ -0,0 +1,45 @@ +import {Component, ViewChild} from '@angular/core'; +import {NavController} from 'ionic-angular'; +import {ProfilePage} from '../profilePage/profilePage'; +import {AuthService} from '../../services/auth.service'; +import {UserInfoService} from '../../services/userInfo.service'; + + +@Component({ + selector: 'page-login', + templateUrl: 'loginPage.html', +}) +export class LoginPage { + public nextPage:any; + public showLoginButton:boolean; + + constructor(public nav: NavController, public auth: AuthService, public userInfoService: UserInfoService) { + this.nextPage = ProfilePage; + this.showLoginButton = false; + } + + public ionViewWillEnter(){ + this.showLoginButton = false; + } + + public ionViewDidEnter(){ + if(this.auth.authenticated()){ + //give some time for local storage to initialize + setTimeout(() => { + this.nav.setRoot(ProfilePage); + }, 1000) + }else{ + this.showLoginButton = true; + } + } + + public login(){ + this.auth.login(() => + setTimeout(() => { + this.nav.setRoot(ProfilePage) + }, 1000)//I need this delay because otherwise the navigation occurs + //before the InAppBrowser closes, and the content ends up under + //the top bar. + ); + } +} diff --git a/Ionic2/src/pages/page1/page1.html b/Ionic2/src/pages/page1/page1.html new file mode 100644 index 0000000..065f74e --- /dev/null +++ b/Ionic2/src/pages/page1/page1.html @@ -0,0 +1,21 @@ + + + + Tab 1 + + + + +

Welcome to Ionic2 beta 11!

+

+ This starter project comes with simple tabs-based layout for apps + that are going to primarily use a Tabbed UI. +

+

+ Take a look at the app/ directory to add or change tabs, + update any existing page or create new pages. +

+ +
diff --git a/Ionic2/src/pages/page1/page1.scss b/Ionic2/src/pages/page1/page1.scss new file mode 100644 index 0000000..50a5175 --- /dev/null +++ b/Ionic2/src/pages/page1/page1.scss @@ -0,0 +1,3 @@ +page-one { + +} diff --git a/Ionic2/src/pages/page1/page1.ts b/Ionic2/src/pages/page1/page1.ts new file mode 100644 index 0000000..d8748b4 --- /dev/null +++ b/Ionic2/src/pages/page1/page1.ts @@ -0,0 +1,16 @@ +import {Component} from '@angular/core'; +import {NavController, NavParams} from 'ionic-angular'; + + +@Component({ + selector: 'page-one', + templateUrl: 'page1.html' +}) +export class Page1 { + public selectedItem:any; + + constructor(public nav: NavController, public navParams: NavParams) { + // If we navigated to this page, we will have an item available as a nav param + this.selectedItem = navParams.get('item'); + } +} diff --git a/Ionic2/src/pages/page3/page3.html b/Ionic2/src/pages/page3/page3.html new file mode 100644 index 0000000..b713fcc --- /dev/null +++ b/Ionic2/src/pages/page3/page3.html @@ -0,0 +1,32 @@ + + + + + Tab 3 + + + + + +

Welcome to Ionic2 beta 11!

+ +

+ This is a primary text. +

+ +

+ And this is a secondary text. +

+ + + + + + + + +
diff --git a/Ionic2/src/pages/page3/page3.scss b/Ionic2/src/pages/page3/page3.scss new file mode 100644 index 0000000..1fd953d --- /dev/null +++ b/Ionic2/src/pages/page3/page3.scss @@ -0,0 +1,3 @@ +page-three { + +} diff --git a/Ionic2/src/pages/page3/page3.ts b/Ionic2/src/pages/page3/page3.ts new file mode 100644 index 0000000..b921d84 --- /dev/null +++ b/Ionic2/src/pages/page3/page3.ts @@ -0,0 +1,12 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'page-three', + templateUrl: 'page3.html' +}) +export class Page3 { + constructor() { + + } +} diff --git a/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.html b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.html new file mode 100644 index 0000000..6c92d68 --- /dev/null +++ b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.html @@ -0,0 +1,34 @@ + + + + + Pending Invites + + + + + + + + + + + +

{{cnt.employment}}

+

{{cnt.education}}

+
+ + + + + + + + + +
+
+ +
diff --git a/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.scss b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.scss new file mode 100644 index 0000000..7d7d1dc --- /dev/null +++ b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.scss @@ -0,0 +1,10 @@ +page-pending-invites { + profile-header{ + div{ + img{ + width: 50px !important; + height: 50px !important; + } + } + } +} diff --git a/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.ts b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.ts new file mode 100644 index 0000000..adf0bf8 --- /dev/null +++ b/Ionic2/src/pages/pendingInvitesPage/pendingInvitesPage.ts @@ -0,0 +1,36 @@ +import {Component} from '@angular/core'; +import {NavController} from 'ionic-angular'; +import {ContactsService} from '../../services/contacts.service'; +import {ContactPage} from '../contactPage/contactPage'; + + +@Component({ + selector: 'page-pending-invites', + templateUrl: 'pendingInvitesPage.html' +}) +export class PendingInvitesPage { + public contacts:any[]; + + constructor(public nav: NavController, public contactsService:ContactsService) { + this.contacts = this.contactsService.getContacts(); + } + + public openInvite(cnt){ + this.nav.push(ContactPage, {contact: cnt}); + } + + public dismissInvite(cnt){ + this._removeContact(cnt); + } + + public acceptInvite(cnt){ + this._removeContact(cnt); + } + + public _removeContact(cnt){ + let index = this.contacts.indexOf(cnt); + if (index > -1) { + this.contacts.splice(index, 1); + } + } +} diff --git a/Ionic2/src/pages/profilePage/profilePage.html b/Ionic2/src/pages/profilePage/profilePage.html new file mode 100644 index 0000000..d914586 --- /dev/null +++ b/Ionic2/src/pages/profilePage/profilePage.html @@ -0,0 +1,53 @@ + + + + + My Profile + + + + + + + + + + + + + + + Interests + + + + Knowledgeable In + + + + Goals + + + + + diff --git a/Ionic2/src/pages/profilePage/profilePage.scss b/Ionic2/src/pages/profilePage/profilePage.scss new file mode 100644 index 0000000..ba029cf --- /dev/null +++ b/Ionic2/src/pages/profilePage/profilePage.scss @@ -0,0 +1,19 @@ +page-profile { + ion-label{ + font-weight: bold; + font-size: medium; + } + ion-input { + input { + margin-left: 0px; + } + } + + /* + hack to prevent border highlight in input elements. + */ + .item-input.ng-valid.input-has-value:not(.input-has-focus) .item-inner { + border-bottom-color: #dedede !important; + box-shadow: none !important; + } +} diff --git a/Ionic2/src/pages/profilePage/profilePage.ts b/Ionic2/src/pages/profilePage/profilePage.ts new file mode 100644 index 0000000..b2fb9e1 --- /dev/null +++ b/Ionic2/src/pages/profilePage/profilePage.ts @@ -0,0 +1,45 @@ +import {Component} from '@angular/core'; +import {UserInfoService} from '../../services/userInfo.service'; + + +@Component({ + selector: 'page-profile', + templateUrl: 'profilePage.html' +}) +export class ProfilePage { + + public isEditMode:boolean; + public fullName:string; + public profileImageUrl:string; + public employment:string; + public education:string; + public interests:string; + public knowledgeable:string; + public currentGoals:string; + + constructor(public userInfo:UserInfoService) { + this.isEditMode = false; + } + + public ionViewWillEnter(){ + this.fullName = this.userInfo.getUserInfo(UserInfoService.PREF_USER_NAME); + this.profileImageUrl = this.userInfo.getUserInfo(UserInfoService.PREF_USER_PICTURE_URL); + this.employment = this.userInfo.getUserInfo(UserInfoService.PREF_USER_EMPLOYMENT); + this.education = this.userInfo.getUserInfo(UserInfoService.PREF_USER_EDUCATION); + this.interests = this.userInfo.getUserInfo(UserInfoService.PREF_USER_INTERESTS); + this.knowledgeable = this.userInfo.getUserInfo(UserInfoService.PREF_USER_KNOWLEDGEABLE_IN); + this.currentGoals = this.userInfo.getUserInfo(UserInfoService.PREF_USER_CURRENT_GOALS); + } + + public edit(){ + if(this.isEditMode){ + //it was in edit mode, so save the changes + this.userInfo.setUserInfo(UserInfoService.PREF_USER_EMPLOYMENT, this.employment); + this.userInfo.setUserInfo(UserInfoService.PREF_USER_EDUCATION, this.education); + this.userInfo.setUserInfo(UserInfoService.PREF_USER_INTERESTS, this.interests); + this.userInfo.setUserInfo(UserInfoService.PREF_USER_KNOWLEDGEABLE_IN, this.knowledgeable); + this.userInfo.setUserInfo(UserInfoService.PREF_USER_CURRENT_GOALS, this.currentGoals); + } + this.isEditMode = !this.isEditMode; + } +} diff --git a/Ionic2/src/pages/settingsPage/settingsPage.html b/Ionic2/src/pages/settingsPage/settingsPage.html new file mode 100644 index 0000000..88668d5 --- /dev/null +++ b/Ionic2/src/pages/settingsPage/settingsPage.html @@ -0,0 +1,42 @@ + + + + + Settings + + + + + + + + General + + + Make me discoverable by others + + + + Notifications for Messages + + + + Notifications for Invites + + + + Account + + + + diff --git a/Ionic2/src/pages/settingsPage/settingsPage.scss b/Ionic2/src/pages/settingsPage/settingsPage.scss new file mode 100644 index 0000000..3c6f099 --- /dev/null +++ b/Ionic2/src/pages/settingsPage/settingsPage.scss @@ -0,0 +1,9 @@ +page-settings { + ion-list-header{ + color: map-get($colors, secondary); + border-bottom: none !important; + ion-label{ + margin-bottom: -5px !important; + } + } +} diff --git a/Ionic2/src/pages/settingsPage/settingsPage.ts b/Ionic2/src/pages/settingsPage/settingsPage.ts new file mode 100644 index 0000000..049f0f8 --- /dev/null +++ b/Ionic2/src/pages/settingsPage/settingsPage.ts @@ -0,0 +1,42 @@ +import {Component} from '@angular/core'; +import {NavController} from 'ionic-angular'; +import {PreferencesService} from '../../services/preferences.service'; +import {AuthService} from '../../services/auth.service'; +import {LoginPage} from '../loginPage/loginPage'; + + +@Component({ + selector: 'page-settings', + templateUrl: 'settingsPage.html' +}) +export class SettingsPage { + public preferences:any; + public PREF_DISCOVERABLE:string; + public PREF_NOTIFY_MESSAGES:string; + public PREF_NOTIFY_INVITES:string; + + constructor(public nav: NavController, public preferencesService:PreferencesService, public auth:AuthService) { + this.preferences = {}; + + this.PREF_DISCOVERABLE = PreferencesService.PREF_DISCOVERABLE; + this.PREF_NOTIFY_MESSAGES = PreferencesService.PREF_NOTIFY_MESSAGES; + this.PREF_NOTIFY_INVITES = PreferencesService.PREF_NOTIFY_INVITES; + } + + public ionViewWillEnter(){ + this.preferences[PreferencesService.PREF_DISCOVERABLE] + = this.preferencesService.getPreference(PreferencesService.PREF_DISCOVERABLE); + this.preferences[PreferencesService.PREF_NOTIFY_MESSAGES] + = this.preferencesService.getPreference(PreferencesService.PREF_NOTIFY_MESSAGES); + this.preferences[PreferencesService.PREF_NOTIFY_INVITES] + = this.preferencesService.getPreference(PreferencesService.PREF_NOTIFY_INVITES); + } + + public changePreference(event, key){ + this.preferencesService.setPreference(key, event.checked); + } + + public logout(){ + this.auth.logout(() => this.nav.setRoot(LoginPage)); + } +} diff --git a/Ionic2/src/services/auth.service.ts b/Ionic2/src/services/auth.service.ts new file mode 100644 index 0000000..0861b02 --- /dev/null +++ b/Ionic2/src/services/auth.service.ts @@ -0,0 +1,180 @@ +import {Storage} from '@ionic/storage'; +import {AuthHttp, JwtHelper, tokenNotExpired} from 'angular2-jwt'; +import {Injectable, NgZone} from '@angular/core'; +import {Observable} from 'rxjs/Rx'; +import {Secret} from '../secrets/secret'; +import {UserInfoService} from './userInfo.service'; + +@Injectable() +export class AuthService { + public jwtHelper:JwtHelper; + public auth0:any; + public lock:any; + public local:Storage; + public zoneImpl:NgZone; + + constructor(public authHttp: AuthHttp, public zone:NgZone, public userInfoService: UserInfoService) { + this.jwtHelper = new JwtHelper(); + this.auth0 = new Auth0({clientID: Secret.AUTH0_CLIENT_ID, domain: Secret.AUTH0_DOMAIN}); + this.lock = new Auth0Lock(Secret.AUTH0_CLIENT_ID, Secret.AUTH0_DOMAIN, { + auth: { + redirect: false, + params: { + scope: 'openid offline_access', + } + } + }); + this.local = new Storage(LocalStorage); + this.refreshSubscription = undefined; + this.user = {}; + + this.userInfoService = userInfoService; + this.zoneImpl = zone; + // Check if there is a profile saved in local storage + this.local.get('profile').then(profile => { + this.user = JSON.parse(profile); + }).catch(error => { + console.log(error); + }); + + this.lock.on('authenticated', authResult => { + this.local.set('id_token', authResult.idToken); + + // Fetch profile information + this.lock.getProfile(authResult.idToken, (error, profile) => { + if (error) { + // Handle error + alert(error); + return; + } + + console.log(profile); + + profile.user_metadata = profile.user_metadata || {}; + + if(this.userInfoService.getUserInfo(UserInfoService.PREF_USER_NAME) === ''){ + //there is no user information stored, store the new info obtained from auth0 + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_NAME, profile.name); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_EMAIL, profile.email); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_AUTH_ID, profile.user_id); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_PICTURE_URL, profile.picture); + } + + this.local.set('profile', JSON.stringify(profile)); + this.user = profile; + }); + + this.lock.hide(); + + this.local.set('refresh_token', authResult.refreshToken); + this.zoneImpl.run(() => this.user = authResult.profile); + + if(this.onAuthenticatedCallback != null){ + this.onAuthenticatedCallback(); + } + }); + + } + + authenticated() { + // Check if there's an unexpired JWT + return tokenNotExpired(); + } + + login(onAuthenticatedCallback) { + // Show the Auth0 Lock widget + this.lock.show(); + this.onAuthenticatedCallback = onAuthenticatedCallback; + } + + logout(onLogOutCallback) { + this.onLogOutCallback = onLogOutCallback; + this.local.remove('profile'); + this.local.remove('id_token'); + this.local.remove('refresh_token'); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_NAME, ''); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_EMAIL, ''); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_AUTH_ID, ''); + this.userInfoService.setUserInfo(UserInfoService.PREF_USER_PICTURE_URL, ''); + this.zoneImpl.run(() => this.user = null); + // Unschedule the token refresh + this.unscheduleRefresh(); + if(this.onLogOutCallback != null){ + this.onLogOutCallback(); + } + } + + scheduleRefresh() { + // If the user is authenticated, use the token stream + // provided by angular2-jwt and flatMap the token + let source = this.authHttp.tokenStream.flatMap( + token => { + // The delay to generate in this case is the difference + // between the expiry time and the issued at time + let jwtIat = this.jwtHelper.decodeToken(token).iat; + let jwtExp = this.jwtHelper.decodeToken(token).exp; + let iat = new Date(0); + let exp = new Date(0); + + let delay = (exp.setUTCSeconds(jwtExp) - iat.setUTCSeconds(jwtIat)); + + return Observable.interval(delay); + }); + + this.refreshSubscription = source.subscribe(() => { + this.getNewJwt(); + }); + } + +startupTokenRefresh() { + // If the user is authenticated, use the token stream + // provided by angular2-jwt and flatMap the token + if (this.authenticated()) { + let source = this.authHttp.tokenStream.flatMap( + token => { + // Get the expiry time to generate + // a delay in milliseconds + let now = new Date().valueOf(); + let jwtExp = this.jwtHelper.decodeToken(token).exp; + let exp = new Date(0); + exp.setUTCSeconds(jwtExp); + let delay = exp.valueOf() - now; + + // Use the delay in a timer to + // run the refresh at the proper time + return Observable.timer(delay); + }); + + // Once the delay time from above is + // reached, get a new JWT and schedule + // additional refreshes + source.subscribe(() => { + this.getNewJwt(); + this.scheduleRefresh(); + }); + } +} + +unscheduleRefresh() { + // Unsubscribe fromt the refresh + if (this.refreshSubscription) { + console.log(this.refreshSubscription); + this.refreshSubscription.unsubscribe(); + } +} + +getNewJwt() { + // Get a new JWT from Auth0 using the refresh token saved + // in local storage + this.local.get('refresh_token').then(token => { + this.auth0.refreshToken(token, (err, delegationRequest) => { + if (err) { + alert(err); + } + this.local.set('id_token', delegationRequest.id_token); + }); + }).catch(error => { + console.log(error); + }); + } +} diff --git a/Ionic2/src/services/contacts.service.ts b/Ionic2/src/services/contacts.service.ts new file mode 100644 index 0000000..028b262 --- /dev/null +++ b/Ionic2/src/services/contacts.service.ts @@ -0,0 +1,41 @@ +import {Injectable} from '@angular/core'; + +import {ContactModel} from '../models/contactModel'; + +@Injectable() +export class ContactsService { + public contacts:any[]; + + constructor(){ + this.contacts = []; + + let cnt = new ContactModel('#1 Gregory', 'House', 'Head of Diagnostic @ PPT Hospital', 'Attended Hopkins University 1979-1984'); + cnt.profileImage = 'build/img/hugh.png' + let cnt2 = new ContactModel('#2 Hugh', 'Laurie', 'Actor, Writer, Director, Author, etc.', 'Attended Selwyn College, Cambridge 1978 - 1984'); + cnt2.profileImage = 'build/img/hugh.png' + let cnt3 = new ContactModel('#3 Gregory', 'House', 'Head of Diagnostic @ PPT Hospital', 'Attended Hopkins University 1979-1984'); + cnt3.profileImage = 'build/img/hugh.png' + let cnt4 = new ContactModel('#4 Hugh', 'Laurie', 'Actor, Writer, Director, Author, etc.', 'Attended Selwyn College, Cambridge 1978 - 1984'); + cnt4.profileImage = 'build/img/hugh.png' + + this.contacts.push(cnt); + this.contacts.push(cnt2); + this.contacts.push(cnt3); + this.contacts.push(cnt4); + } + + public removeContact(cnt){ + let index = this.contacts.indexOf(cnt); + if (index > -1) { + this.contacts.splice(index, 1); + } + } + + public getContacts(){ + return [...this.contacts]; + } + + public getNearbyUsers(){ + return [...this.contacts]; + } +} diff --git a/Ionic2/src/services/preferences.service.ts b/Ionic2/src/services/preferences.service.ts new file mode 100644 index 0000000..9afb94d --- /dev/null +++ b/Ionic2/src/services/preferences.service.ts @@ -0,0 +1,73 @@ +import {Injectable} from '@angular/core'; +import {StorageService} from './storage.service'; + +@Injectable() +export class PreferencesService { + + static get PREF_INITIALIZED() { return 'preferencesInitialized';} + static get PREF_DISCOVERABLE() { return 'pref_discoverable';} + static get PREF_NOTIFY_MESSAGES() { return 'pref_notification_messages';} + static get PREF_NOTIFY_INVITES() { return 'pref_notification_invites';} + + public _preferences:any; + + constructor(public _storageService: StorageService) { + this._preferences = {}; + } + + public initializePreferences(){ + console.log('initializePreferences'); + this._storageService.storage.get(PreferencesService.PREF_INITIALIZED).then((result) => { + if(result == null || result == false){ + console.log('initializePreferences with default values'); + this._storageService.storage.set(PreferencesService.PREF_INITIALIZED, true); + this._storageService.storage.set(PreferencesService.PREF_DISCOVERABLE, true); + this._storageService.storage.set(PreferencesService.PREF_NOTIFY_MESSAGES, true); + this._storageService.storage.set(PreferencesService.PREF_NOTIFY_INVITES, true); + + //initialize in memory preferences + this._preferences[PreferencesService.PREF_DISCOVERABLE] = true; + this._preferences[PreferencesService.PREF_NOTIFY_MESSAGES] = true; + this._preferences[PreferencesService.PREF_NOTIFY_INVITES] = true; + }else{ + console.log('preferences obtained from storage'); + let prefs = + [ + PreferencesService.PREF_DISCOVERABLE, + PreferencesService.PREF_NOTIFY_MESSAGES, + PreferencesService.PREF_NOTIFY_INVITES + ]; + + let thisRef = this; + this._getAllPreferences(prefs).then(function(results){ + //initialize in memory preferences + for(let i = 0; i < prefs.length; i++){ + thisRef._preferences[prefs[i]] = results[i]; + } + }, function (err) { + // If any of the preferences fail to read, err is the first error + console.log(err); + }); + } + }); + } + + public getPreference(key){ + return this._preferences[key]; + } + + public setPreference(key, value){ + this._preferences[key] = value;//update pref in memory + this._storageService.storage.set(key, value);//update pref in db + } + + public _getAllPreferences(prefs){ + return Promise.all(prefs.map((key) => { + return this._storageService.storage.get(key); + })); + } + + public _getPreference(key){ + return this._storageService.storage.get(key); + } +} diff --git a/Ionic2/src/services/storage.service.ts b/Ionic2/src/services/storage.service.ts new file mode 100644 index 0000000..c6e6480 --- /dev/null +++ b/Ionic2/src/services/storage.service.ts @@ -0,0 +1,11 @@ +import {Storage} from '@ionic/storage'; +import {Injectable} from '@angular/core'; + +@Injectable() +export class StorageService { + public storage:Storage; + + constructor() { + this.storage = new Storage(); + } +} diff --git a/Ionic2/src/services/userInfo.service.ts b/Ionic2/src/services/userInfo.service.ts new file mode 100644 index 0000000..663c351 --- /dev/null +++ b/Ionic2/src/services/userInfo.service.ts @@ -0,0 +1,110 @@ +import {Injectable} from '@angular/core'; +import {StorageService} from './storage.service'; + +@Injectable() +export class UserInfoService { + + static get PREF_INITIALIZED() { return 'pref_user_info_initialized';} + static get PREF_USER_NAME() { return 'pref_user_name';} + static get PREF_USER_EMAIL() { return 'pref_user_email';} + static get PREF_USER_AUTH_ID() { return 'pref_user_auth_id';} + static get PREF_USER_PICTURE_URL() { return 'pref_user_picture_url';} + + static get PREF_USER_EMPLOYMENT() { return 'pref_user_employment';} + static get PREF_USER_EDUCATION() { return 'pref_user_education';} + static get PREF_USER_KNOWLEDGEABLE_IN() { return 'pref_user_knowledgeable_in';} + static get PREF_USER_INTERESTS() { return 'pref_user_interests';} + static get PREF_USER_CURRENT_GOALS() { return 'pref_user_current_goals';} + + public _userInfo:any; + public _keys:string[]; + + constructor(public _storageService:StorageService) { + this._userInfo = {}; + + this._keys = [ + UserInfoService.PREF_USER_NAME, + UserInfoService.PREF_USER_EMAIL, + UserInfoService.PREF_USER_AUTH_ID, + UserInfoService.PREF_USER_PICTURE_URL, + UserInfoService.PREF_USER_EMPLOYMENT, + UserInfoService.PREF_USER_EDUCATION, + UserInfoService.PREF_USER_KNOWLEDGEABLE_IN, + UserInfoService.PREF_USER_INTERESTS, + UserInfoService.PREF_USER_CURRENT_GOALS, + ]; + } + + public initialize(){ + console.log('initialize user info'); + this._storageService.storage.get(UserInfoService.PREF_INITIALIZED).then((result) => { + if(result == null || result == false){ + console.log('initialize user info with default values'); + this._storageService.storage.set(UserInfoService.PREF_INITIALIZED, true); + this._storageService.storage.set(UserInfoService.PREF_USER_NAME, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_EMAIL, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_AUTH_ID, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_PICTURE_URL, ''); + + this._storageService.storage.set(UserInfoService.PREF_USER_EMPLOYMENT, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_EDUCATION, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_KNOWLEDGEABLE_IN, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_INTERESTS, ''); + this._storageService.storage.set(UserInfoService.PREF_USER_CURRENT_GOALS, ''); + + + //initialize in memory + this._userInfo[UserInfoService.PREF_USER_NAME] = ''; + this._userInfo[UserInfoService.PREF_USER_EMAIL] = ''; + this._userInfo[UserInfoService.PREF_USER_AUTH_ID] = ''; + this._userInfo[UserInfoService.PREF_USER_PICTURE_URL] = ''; + + this._userInfo[UserInfoService.PREF_USER_EMPLOYMENT] = ''; + this._userInfo[UserInfoService.PREF_USER_EDUCATION] = ''; + this._userInfo[UserInfoService.PREF_USER_KNOWLEDGEABLE_IN] = ''; + this._userInfo[UserInfoService.PREF_USER_INTERESTS] = ''; + this._userInfo[UserInfoService.PREF_USER_CURRENT_GOALS] = ''; + }else{ + console.log('user info obtained from storage'); + + let thisRef = this; + this._getAllUserInfo(this._keys).then(function(results){ + //initialize in memory user information + for(let i = 0; i < thisRef._keys.length; i++){ + thisRef._userInfo[thisRef._keys[i]] = results[i]; + } + console.log('stored user info:'); + console.log(thisRef._userInfo); + }, function (err) { + // If any of the information fail to read, err is the first error + console.log(err); + }); + } + }); + } + + /* retrieve stored user information */ + public getAllUserInfo(){ + return this._getAllUserInfo(this._keys); + } + + public getUserInfo(key){ + return this._userInfo[key]; + } + + public setUserInfo(key, value){ + this._userInfo[key] = value;//update in memory + this._storageService.storage.set(key, value);//update in db + } + + public _getAllUserInfo(keys){ + return Promise.all(keys.map((key) => { + return this._storageService.storage.get(key); + })); + } + + public _getUserInfo(key){ + return this._storageService.storage.get(key); + } + +} diff --git a/Ionic2/src/theme/global.scss b/Ionic2/src/theme/global.scss index 782f2ce..4ec0e35 100644 --- a/Ionic2/src/theme/global.scss +++ b/Ionic2/src/theme/global.scss @@ -2,7 +2,7 @@ // Global CSS -// -------------------------------------------------- +// -------------------------------------------------- // Put CSS rules here that you want to apply globally. // // To declare rules for a specific mode, create a child rule @@ -10,3 +10,51 @@ // automatically applied to the element in the app. // // App Shared Sass variables belong in app.variables.scss. + +.profile-header { + font-size: $header-font-size; + font-weight: $header-font-weight; + margin-left: 10px; + line-height: 100%; +} + +.content-center-center { + display: flex; + align-items: center; + justify-content: center; +} + +.content-center-left { + display: flex; + align-items: center; + justify-content: flex-start;; +} + +.content-center-right { + display: flex; + align-items: center; + justify-content: flex-end; +} + +ion-item{ + .text-input { + font-size: large; + } +} + +//makes the paragraph properly wrap the text inside it in multiple lines +p { + word-wrap: break-word; + white-space: normal; +} + +ion-menu{ + ion-content{ + ion-list{ + ion-icon{ + margin-right: 20px; + color: #595959; + } + } + } +} diff --git a/Ionic2/src/theme/variables.scss b/Ionic2/src/theme/variables.scss index 0ab6bf7..c8798b8 100644 --- a/Ionic2/src/theme/variables.scss +++ b/Ionic2/src/theme/variables.scss @@ -11,7 +11,16 @@ // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/ $text-color: #000; -$background-color: #fff; + +$background-ios-color: #FAFAFA; +$background-md-color: #FAFAFA; +$background-wp-color: #FAFAFA; + +$list-background-color: #FAFAFA; + +$header-font-size: 2.2rem; +$header-font-weight: 500; + // Named Color Variables @@ -23,14 +32,40 @@ $background-color: #fff; // The "primary" color is the only required color in the map. $colors: ( - primary: #387ef5, - secondary: #32db64, + dark-primary: #1976D2, + primary: #2196F3, + light: #BBDEFB, + text-icons: #FFFFFF, + secondary: #FF5722, + primary-text: #212121, + secondary-text: #727272, + divider: #B6B6B6, + background-color: #FAFAFA, + danger: #f53d3d, - light: #f4f4f4, dark: #222, - favorite: #69BB7B + favorite: #8bc34a, /* #69BB7B */ + + facebook: #3b5998, + googleplus: #dc4e41, + linkedin: #0077b5, +); + +$chat-bubble:( + background-left: #fea21c, + background-right: #8abefa, ); +//To make the input use the secondary color when active, instead of primary +//https://github.com/driftyco/ionic/blob/2.0/src/components/input/input.md.scss +//https://github.com/driftyco/ionic/blob/2.0/src/components/label/label.md.scss +$label-ios-text-color-focused: map-get($colors, secondary); +$label-md-text-color-focused: map-get($colors, secondary); +$label-wp-text-color-focused: map-get($colors, secondary); + +$text-input-ios-highlight-color: map-get($colors, secondary); +$text-input-md-highlight-color: map-get($colors, secondary); +$text-input-wp-highlight-color: map-get($colors, secondary); // App Theme // --------------------------------------------------