diff --git a/packages/demos/conference-app/angular/package-lock.json b/packages/demos/conference-app/angular/package-lock.json index 4f74267f8e..87d4f45f2d 100644 --- a/packages/demos/conference-app/angular/package-lock.json +++ b/packages/demos/conference-app/angular/package-lock.json @@ -292,9 +292,9 @@ } }, "@ionic/core": { - "version": "0.0.2-36", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-0.0.2-36.tgz", - "integrity": "sha512-pKzFN8elEPsOaugopar148GM4E88tzd3Q523XEyhOUsUP4NKD+B0bpro3OwDmvLlv6U7z0lrz9jBCs3ZIBeIUA==" + "version": "0.0.2-37", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-0.0.2-37.tgz", + "integrity": "sha512-Tx4yFHqJMzfb8KBtp/dk/1GD+0SUnjlycqk3Egtw43a+4t8XSyJRbcjr1tAeH622TRntj6mLTDRwJ86MPJQMGQ==" }, "@ionic/storage": { "version": "2.1.3", diff --git a/packages/demos/conference-app/angular/src/app/app.component.html b/packages/demos/conference-app/angular/src/app/app.component.html index 87f58eca29..c6fb59b742 100644 --- a/packages/demos/conference-app/angular/src/app/app.component.html +++ b/packages/demos/conference-app/angular/src/app/app.component.html @@ -1,3 +1,29 @@ - + + + + + + Menu + + + + + + Navigate + + + + + {{p.title}} + + + + + + + + + + \ No newline at end of file diff --git a/packages/demos/conference-app/angular/src/app/app.component.ts b/packages/demos/conference-app/angular/src/app/app.component.ts index f95e79ad79..e49697a4d6 100644 --- a/packages/demos/conference-app/angular/src/app/app.component.ts +++ b/packages/demos/conference-app/angular/src/app/app.component.ts @@ -1,12 +1,179 @@ -import { Component } from '@angular/core'; +import { + Component, + ElementRef, + OnInit, + ViewChild +} from '@angular/core'; +import { + Events, + MenuController, +} from '@ionic/angular'; +import { Storage } from '@ionic/storage'; + +import { AboutPage } from '../pages/about/about'; +import { AccountPage } from '../pages/account/account'; +import { LoginPage } from '../pages/login/login'; +import { MapPage } from '../pages/map/map'; +import { SignupPage } from '../pages/signup/signup'; import { TabsPage } from '../pages/tabs-page/tabs-page'; +import { TutorialPage } from '../pages/tutorial/tutorial'; +import { SchedulePage } from '../pages/schedule/schedule'; +import { SpeakerListPage } from '../pages/speaker-list/speaker-list'; +import { SupportPage } from '../pages/support/support'; + +import { ConferenceData } from '../providers/conference-data'; +import { UserData } from '../providers/user-data'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent { - rootPage = TabsPage; +export class AppComponent implements OnInit { + + @ViewChild('nav') navRef: ElementRef; + rootPage: any; + + // List of pages that can be navigated to from the left menu + // the left menu only works after login + // the login page disables the left menu + appPages: PageInterface[] = [ + { title: 'Schedule', name: 'TabsPage', component: TabsPage, tabComponent: SchedulePage, index: 0, icon: 'calendar' }, + { title: 'Speakers', name: 'TabsPage', component: TabsPage, tabComponent: SpeakerListPage, index: 1, icon: 'contacts' }, + { title: 'Map', name: 'TabsPage', component: TabsPage, tabComponent: MapPage, index: 2, icon: 'map' }, + { title: 'About', name: 'TabsPage', component: TabsPage, tabComponent: AboutPage, index: 3, icon: 'information-circle' } + ]; + + loggedInPages: PageInterface[] = [ + { title: 'Account', name: 'AccountPage', component: AccountPage, icon: 'person' }, + { title: 'Support', name: 'SupportPage', component: SupportPage, icon: 'help' }, + { title: 'Logout', name: 'TabsPage', component: TabsPage, icon: 'log-out', logsOut: true } + ]; + + loggedOutPages: PageInterface[] = [ + { title: 'Login', name: 'LoginPage', component: LoginPage, icon: 'log-in' }, + { title: 'Support', name: 'SupportPage', component: SupportPage, icon: 'help' }, + { title: 'Signup', name: 'SignupPage', component: SignupPage, icon: 'person-add' } + ]; + + constructor( + public confData: ConferenceData, + public events: Events, + public menu: MenuController, + public storage: Storage, + public userData: UserData) { + } + + ngOnInit() { + // Check if the user has already seen the tutorial + this.storage.get('hasSeenTutorial') + .then((hasSeenTutorial) => { + if (hasSeenTutorial) { + this.rootPage = TabsPage; + } else { + this.rootPage = TutorialPage; + } + // this.platformReady() + }); + + // load the conference data + this.confData.load(); + + // decide which menu items should be hidden by current login status stored in local storage + this.userData.hasLoggedIn().then((hasLoggedIn) => { + this.enableMenu(hasLoggedIn === true); + }); + this.enableMenu(true); + + this.listenToLoginEvents(); + } + + openPage(page: PageInterface) { + let params = {}; + + // the nav component was found using @ViewChild(Nav) + // setRoot on the nav to remove previous pages and only have this page + // we wouldn't want the back button to show in this scenario + if (page.index) { + params = { tabIndex: page.index }; + } + + // If we are already on tabs just change the selected tab + // don't setRoot again, this maintains the history stack of the + // tabs even if changing them from the menu + /*if (getNav(this.navRef).getChildNavs().length && page.index !== undefined) { + getNav(this.navRef).getChildNavs()[0].select(page.index); + } else { + // Set the root of the nav with params if it's a tab index + getNav(this.navRef).setRoot(page.name, params).catch((err: any) => { + console.log(`Didn't set nav root: ${err}`); + }); + } + */ + console.log('TODO DANNNNNNN'); + + + if (page.logsOut === true) { + // Give the menu time to close before changing to logged out + this.userData.logout(); + } + } + + openTutorial() { + return getNav(this.navRef).setRoot(TutorialPage); + } + + listenToLoginEvents() { + this.events.subscribe('user:login', () => { + this.enableMenu(true); + }); + + this.events.subscribe('user:signup', () => { + this.enableMenu(true); + }); + + this.events.subscribe('user:logout', () => { + this.enableMenu(false); + }); + } + + enableMenu(loggedIn: boolean) { + this.menu.enable(loggedIn, 'loggedInMenu'); + this.menu.enable(!loggedIn, 'loggedOutMenu'); + } + + isActive(page: PageInterface) { + return 'primary'; + //const childNav = getNav(this.navRef).getChildNavs()[0]; + + // Tabs are a special case because they have their own navigation + /*if (childNav) { + if (childNav.getSelected() && childNav.getSelected().root === page.tabComponent) { + return 'primary'; + } + return; + } + + if (this.nav.getActive() && this.nav.getActive().name === page.name) { + return 'primary'; + } + return; + */ + } +} + +function getNav(elementRef: ElementRef) { + return elementRef.nativeElement as HTMLIonNavElement; +} + +export interface PageInterface { + title: string; + name: string; + component: any; + icon: string; + logsOut?: boolean; + index?: number; + tabName?: string; + tabComponent?: any; } diff --git a/packages/demos/conference-app/angular/src/app/app.module.ts b/packages/demos/conference-app/angular/src/app/app.module.ts index 84a667ed31..7e403ab24f 100644 --- a/packages/demos/conference-app/angular/src/app/app.module.ts +++ b/packages/demos/conference-app/angular/src/app/app.module.ts @@ -1,5 +1,6 @@ import { BrowserModule, } from '@angular/platform-browser'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { IonicAngularModule } from '@ionic/angular'; @@ -11,13 +12,18 @@ import { AppComponent } from './app.component'; import { AboutPage } from '../pages/about/about'; import { PopoverPage } from '../pages/about-popover/about-popover'; +import { AccountPage } from '../pages/account/account'; +import { LoginPage } from '../pages/login/login'; import { MapPage } from '../pages/map/map'; import { SchedulePage } from '../pages/schedule/schedule'; import { ScheduleFilterPage } from '../pages/schedule-filter/schedule-filter'; import { SessionDetailPage } from '../pages/session-detail/session-detail'; +import { SignupPage } from '../pages/signup/signup'; import { SpeakerDetailPage } from '../pages/speaker-detail/speaker-detail'; import { SpeakerListPage } from '../pages/speaker-list/speaker-list'; +import { SupportPage } from '../pages/support/support'; import { TabsPage } from '../pages/tabs-page/tabs-page'; +import { TutorialPage } from '../pages/tutorial/tutorial'; import { ConferenceData } from '../providers/conference-data'; import { UserData } from '../providers/user-data'; @@ -25,29 +31,40 @@ import { UserData } from '../providers/user-data'; @NgModule({ declarations: [ AboutPage, + AccountPage, AppComponent, + LoginPage, MapPage, PopoverPage, SchedulePage, ScheduleFilterPage, SessionDetailPage, + SignupPage, SpeakerDetailPage, SpeakerListPage, - TabsPage + SupportPage, + TabsPage, + TutorialPage ], entryComponents: [ AboutPage, + AccountPage, + LoginPage, MapPage, PopoverPage, SchedulePage, ScheduleFilterPage, SessionDetailPage, + SignupPage, SpeakerDetailPage, SpeakerListPage, - TabsPage + SupportPage, + TabsPage, + TutorialPage ], imports: [ BrowserModule, + FormsModule, HttpModule, IonicAngularModule.forRoot(), IonicStorageModule.forRoot() diff --git a/packages/demos/conference-app/angular/src/pages/account/account.html b/packages/demos/conference-app/angular/src/pages/account/account.html new file mode 100644 index 0000000000..299e180f8d --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/account/account.html @@ -0,0 +1,25 @@ + + + + + + + + Account + + + + +
+ avatar +

{{username}}

+ + + Update Picture + Change Username + Change Password + Support + Logout + +
+
diff --git a/packages/demos/conference-app/angular/src/pages/account/account.scss b/packages/demos/conference-app/angular/src/pages/account/account.scss new file mode 100644 index 0000000000..5cf26c3612 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/account/account.scss @@ -0,0 +1,5 @@ + +page-account img { + max-width: 140px; + border-radius: 50%; +} diff --git a/packages/demos/conference-app/angular/src/pages/account/account.ts b/packages/demos/conference-app/angular/src/pages/account/account.ts new file mode 100644 index 0000000000..c567f78b4a --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/account/account.ts @@ -0,0 +1,73 @@ +import { Component } from '@angular/core'; + +import { AlertController, NavController } from '@ionic/angular'; + +import { UserData } from '../../providers/user-data'; + + +@Component({ + selector: 'page-account', + templateUrl: 'account.html', + styleUrls: ['./account.scss'] +}) +export class AccountPage { + username: string; + + constructor(public alertCtrl: AlertController, public nav: NavController, public userData: UserData) { + + } + + ngAfterViewInit() { + this.getUsername(); + } + + updatePicture() { + console.log('Clicked to update picture'); + } + + // Present an alert with the current username populated + // clicking OK will update the username and display it + // clicking Cancel will close the alert and do nothing + changeUsername() { + const alert = this.alertCtrl.create({ + title: 'Change Username', + buttons: [ + 'Cancel', + { + text: 'Ok', + handler: (data: any) => { + this.userData.setUsername(data.username); + this.getUsername(); + } + } + ], + inputs: [ + { + name: 'username', + value: this.username, + placeholder: 'username' + } + ] + }); + alert.present(); + } + + getUsername() { + this.userData.getUsername().then((username) => { + this.username = username; + }); + } + + changePassword() { + console.log('Clicked to change password'); + } + + logout() { + this.userData.logout(); + this.nav.setRoot('LoginPage'); + } + + support() { + this.nav.push('SupportPage'); + } +} diff --git a/packages/demos/conference-app/angular/src/pages/login/login.html b/packages/demos/conference-app/angular/src/pages/login/login.html new file mode 100644 index 0000000000..c82a80edc5 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/login/login.html @@ -0,0 +1,50 @@ + + + + + + + + + Login + + + + + + +
+ + + Username + + + +

+ Username is required +

+ + + Password + + + +

+ Password is required +

+
+ + + + + + + + + +
+ +
diff --git a/packages/demos/conference-app/angular/src/pages/login/login.scss b/packages/demos/conference-app/angular/src/pages/login/login.scss new file mode 100644 index 0000000000..9ca4c26fe3 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/login/login.scss @@ -0,0 +1,18 @@ +// Styles for all of the user pages: +// Login, Signup, Support + +page-user { + .logo { + padding: 20px 0; + min-height: 200px; + text-align: center; + + img { + max-width: 150px; + } + } + + .list { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/packages/demos/conference-app/angular/src/pages/login/login.ts b/packages/demos/conference-app/angular/src/pages/login/login.ts new file mode 100644 index 0000000000..a0624d3f21 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/login/login.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { NavController } from '@ionic/angular'; + +import { UserData } from '../../providers/user-data'; + +import { UserOptions } from '../../interfaces/user-options'; + +import { TabsPage } from '../tabs-page/tabs-page'; +import { SignupPage } from '../signup/signup'; + + +@Component({ + selector: 'page-user', + templateUrl: 'login.html', + styleUrls: ['./login.scss'] +}) +export class LoginPage { + login: UserOptions = { username: '', password: '' }; + submitted = false; + + constructor(public navCtrl: NavController, public userData: UserData) { } + + onLogin(form: NgForm) { + this.submitted = true; + + if (form.valid) { + this.userData.login(this.login.username); + this.navCtrl.push(TabsPage); + } + } + + onSignup() { + this.navCtrl.push(SignupPage); + } +} diff --git a/packages/demos/conference-app/angular/src/pages/signup/signup.html b/packages/demos/conference-app/angular/src/pages/signup/signup.html new file mode 100644 index 0000000000..d87e8dfb6c --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/signup/signup.html @@ -0,0 +1,44 @@ + + + + + + + + Signup + + + + + + + +
+ + + Username + + + +

+ Username is required +

+ + + Password + + + +

+ Password is required +

+
+ +
+ Create +
+
+ +
diff --git a/packages/demos/conference-app/angular/src/pages/signup/signup.scss b/packages/demos/conference-app/angular/src/pages/signup/signup.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/demos/conference-app/angular/src/pages/signup/signup.ts b/packages/demos/conference-app/angular/src/pages/signup/signup.ts new file mode 100644 index 0000000000..a0480b1241 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/signup/signup.ts @@ -0,0 +1,32 @@ +import { Component } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { NavController } from '@ionic/angular'; + +import { UserData } from '../../providers/user-data'; + +import { UserOptions } from '../../interfaces/user-options'; + +import { TabsPage } from '../tabs-page/tabs-page'; + + +@Component({ + selector: 'page-user', + templateUrl: 'signup.html', + styleUrls: ['./signup.scss'] +}) +export class SignupPage { + signup: UserOptions = { username: '', password: '' }; + submitted = false; + + constructor(public navCtrl: NavController, public userData: UserData) {} + + onSignup(form: NgForm) { + this.submitted = true; + + if (form.valid) { + this.userData.signup(this.signup.username); + this.navCtrl.push(TabsPage); + } + } +} diff --git a/packages/demos/conference-app/angular/src/pages/support/support.html b/packages/demos/conference-app/angular/src/pages/support/support.html new file mode 100644 index 0000000000..565844764e --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/support/support.html @@ -0,0 +1,35 @@ + + + + + + + + + Support + + + + + + + +
+ + + Enter your support message below + + + + +

+ Support message is required +

+ +
+ Submit +
+
+
diff --git a/packages/demos/conference-app/angular/src/pages/support/support.scss b/packages/demos/conference-app/angular/src/pages/support/support.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/demos/conference-app/angular/src/pages/support/support.ts b/packages/demos/conference-app/angular/src/pages/support/support.ts new file mode 100644 index 0000000000..e4e678f611 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/support/support.ts @@ -0,0 +1,69 @@ +import { Component } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { AlertController, NavController, ToastController } from '@ionic/angular'; + + +@Component({ + selector: 'page-user', + templateUrl: 'support.html', + styleUrls: ['./support.scss'] +}) +export class SupportPage { + + submitted = false; + supportMessage: string; + + constructor( + public navCtrl: NavController, + public alertCtrl: AlertController, + public toastCtrl: ToastController + ) { + + } + + ionViewDidEnter() { + const toast = this.toastCtrl.create({ + message: 'This does not actually send a support request.', + duration: 3000 + }); + toast.present(); + } + + submit(form: NgForm) { + this.submitted = true; + + if (form.valid) { + this.supportMessage = ''; + this.submitted = false; + + const toast = this.toastCtrl.create({ + message: 'Your support request has been sent.', + duration: 3000 + }); + toast.present(); + } + } + + // If the user enters text in the support question and then navigates + // without submitting first, ask if they meant to leave the page + ionViewCanLeave(): boolean | Promise { + // If the support message is empty we should just navigate + if (!this.supportMessage || this.supportMessage.trim().length === 0) { + return true; + } + + return new Promise((resolve: any, reject: any) => { + const alert = this.alertCtrl.create({ + title: 'Leave this page?', + message: 'Are you sure you want to leave this page? Your support message will not be submitted.', + buttons: [ + { text: 'Stay', handler: reject }, + { text: 'Leave', role: 'cancel', handler: resolve } + ] + }); + + alert.present(); + }); + } +} diff --git a/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.html b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.html new file mode 100644 index 0000000000..52692cddff --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.html @@ -0,0 +1,44 @@ + + + + Skip + + + + + + + + + +

+ Welcome to ICA +

+

+ The ionic conference app is a practical preview of the ionic framework in action, and a demonstration of proper code use. +

+
+ + + +

What is Ionic?

+

Ionic Framework is an open source SDK that enables developers to build high quality mobile apps with web technologies like HTML, CSS, and JavaScript.

+
+ + + +

What is Ionic Pro?

+

Ionic Pro is a powerful set of services and features built on top of Ionic Framework that brings a totally new level of app development agility to mobile dev teams.

+
+ + + +

Ready to Play?

+ + Continue + + +
+ +
+
diff --git a/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.scss b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.scss new file mode 100644 index 0000000000..95ae306ee3 --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.scss @@ -0,0 +1,37 @@ + +page-tutorial { + .toolbar-background { + background: transparent; + border-color: transparent; + } + + .slide-zoom { + height: 100%; + } + + .slide-title { + margin-top: 2.8rem; + } + + .slide-image { + max-height: 50%; + max-width: 60%; + margin: 36px 0; + } + + b { + font-weight: 500; + } + + p { + padding: 0 40px; + font-size: 14px; + line-height: 1.5; + color: #60646B; + + b { + color: #000000; + } + } + +} diff --git a/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.ts b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.ts new file mode 100644 index 0000000000..3d66303b5d --- /dev/null +++ b/packages/demos/conference-app/angular/src/pages/tutorial/tutorial.ts @@ -0,0 +1,61 @@ +import { + Component, + ElementRef, + ViewChild +} from '@angular/core'; + +import { MenuController, NavController } from '@ionic/angular'; + +import { Storage } from '@ionic/storage'; + +import { TabsPage } from '../tabs-page/tabs-page'; + +@Component({ + selector: 'page-tutorial', + templateUrl: 'tutorial.html' +}) + +export class TutorialPage { + showSkip = true; + + @ViewChild('slides') slidesRef: ElementRef; + + constructor( + public navCtrl: NavController, + public menu: MenuController, + public storage: Storage + ) { } + + startApp() { + this.navCtrl.push(TabsPage).then(() => { + this.storage.set('hasSeenTutorial', 'true'); + }); + } + + onSlideChangeStart(slider: any) { + this.showSkip = !slider.isEnd(); + } + + ionViewWillEnter() { + return getHydratedSlides(this.slidesRef).then((slides) => { + slides.update(); + }); + } + + ionViewDidEnter() { + // the root left menu should be disabled on the tutorial page + this.menu.enable(false); + } + + ionViewDidLeave() { + // enable the root left menu when leaving the tutorial page + this.menu.enable(true); + } + +} + +function getHydratedSlides(slidesRef: ElementRef): Promise { + return slidesRef.nativeElement.componentOnReady().then(() => { + return slidesRef.nativeElement as HTMLIonSlidesElement; + }); +}