From 67bfe4a20014576b0fa3f03bc035bdd86274891a Mon Sep 17 00:00:00 2001 From: Jose Ignacio Santa Cruz G Date: Mon, 15 May 2017 20:07:25 -0400 Subject: [PATCH] ADD GLobalScope and Shryriiwook Services. Multiple adjustments for the looks and the workings :P Signed-off-by: Jose Ignacio Santa Cruz G --- README.md | 13 +- config.xml | 33 +++-- package.json | 6 +- src/app/app.component.ts | 128 ++++++++++++----- src/app/app.html | 28 ++-- src/app/app.module.ts | 8 +- src/app/app.scss | 92 ++++++------ .../custom-header/custom-header.html | 37 ++--- .../custom-header/custom-header.module.ts | 8 +- src/components/custom-header/custom-header.ts | 131 ++++++++++++++---- src/index.html | 16 ++- src/pages/about/about.ts | 14 +- src/pages/base/base.ts | 19 +++ src/pages/credits/credits.scss | 16 +++ src/pages/credits/credits.ts | 11 +- src/pages/joke/joke.html | 17 ++- src/pages/joke/joke.module.ts | 7 +- src/pages/joke/joke.scss | 15 ++ src/pages/joke/joke.ts | 74 +++++++--- src/pages/more-info/more-info.scss | 11 +- src/pages/more-info/more-info.ts | 67 ++++----- src/pages/splash/splash.ts | 11 +- src/pipes/shyriiwook/shyriiwook.ts | 46 ++++++ .../global-scope-service.ts | 94 +++++++++++++ .../sw-api-service/sw-api-service.ts | 29 +++- src/providers/sw-joke/sw-joke.ts | 5 +- 26 files changed, 708 insertions(+), 228 deletions(-) create mode 100644 src/pages/base/base.ts create mode 100644 src/pipes/shyriiwook/shyriiwook.ts create mode 100644 src/providers/global-scope-service/global-scope-service.ts diff --git a/README.md b/README.md index 549439a..20ecf45 100644 --- a/README.md +++ b/README.md @@ -14,24 +14,25 @@ Don' t know if really nice, but I like them... * Lazy loading for pages * SVG icons * Lodash -* InAppBrowser for external links +* InAppBrowser for external links (solved iOS issue, due to target not optional) +* Shyriiwook translator (Chewie mode) **_NEW_** ## TODO To resolve sometime, in the future... * Un-hardcode the joke list * Unit & E2E tests -* Add an "Affiliation" option to choose from Jedi/Sith/or none -* Implement the real wookie mode, to apply joke translation to Shyriiwook +* ~~Add an _"Affiliation"_ option to choose from _Jedi/Sith/or none_~~ **DONE** +* ~~Implement the real wookie mode, to apply joke translation to Shyriiwook~~ **DONE** * Add app languages -* Use PouchDB for local cache. +* Use _PouchDB_ for local cache. Had most of the TODO items in mind, but I really need to sleep. Please report any issues. This is my first attempt for a full Ionic2 app, so bugs & improvements to be expected. -*Twitter*: @JSantaCL | -*Medium*: https://medium.com/@jsantacl +*Twitter*: @JSantaCL +
*Medium*: https://medium.com/@jsantacl diff --git a/config.xml b/config.xml index efe08e8..cf9b963 100644 --- a/config.xml +++ b/config.xml @@ -6,6 +6,7 @@ + @@ -18,10 +19,13 @@ - - + - + + + + + @@ -77,13 +81,22 @@ - - - - - - - + + + + + + + + + + + + + + + + diff --git a/package.json b/package.json index b477a72..e6cae26 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,11 @@ "@ionic-native/core": "^3.6.1", "@ionic-native/in-app-browser": "^3.6.1", "@ionic-native/splash-screen": "3.4.2", + "@ionic-native/sqlite": "^3.7.0", "@ionic-native/status-bar": "3.4.2", "@ionic/storage": "2.0.1", "ionic-angular": "3.1.1", + "ionic-cache": "^2.0.1", "ionicons": "3.0.0", "lodash": "^4.17.4", "rxjs": "5.1.1", @@ -33,8 +35,8 @@ }, "devDependencies": { "@ionic/app-scripts": "1.3.6", - "@ionic/cli-plugin-cordova": "^1.0.0-rc.2", - "@ionic/cli-plugin-ionic-angular": "^1.0.0-rc.2", + "@ionic/cli-plugin-cordova": "^1.0.0", + "@ionic/cli-plugin-ionic-angular": "^1.0.0", "typescript": "~2.2.1" }, "description": "A brief (and probably boring collection) of anotated StarWars jokes. Created for the Ionic Jedi Hackster (hackathon 2017)." diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e78c17d..5891b7a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,61 +1,123 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, EventEmitter, Output } from '@angular/core'; import { Platform, NavController } from 'ionic-angular'; import { StatusBar } from '@ionic-native/status-bar'; import { SplashScreen } from '@ionic-native/splash-screen'; +import { GlobalScopeService } from "../providers/global-scope-service/global-scope-service"; @Component({ templateUrl: 'app.html' }) export class MyApp { - rootPage: string = 'SplashPage'; - pages: Array < { - title: string, - name: string - } > - @ViewChild('content') nav: NavController; + rootPage: string = 'SplashPage'; + @ViewChild('content') nav: NavController; public isJedi: boolean; - public isSith: boolean; - - constructor(private platform: Platform, private statusBar: StatusBar, private splashScreen: SplashScreen) { - this.initializeApp(); - this.pages = [{ - title: 'Splash', - name: 'Splash' - }, - { - title: 'Credits', - name: 'Credits' - }, - { - title: 'Search', - name: 'Search' - } - ]; + private isJedi$: any; + public affiliation: string; + public affiliationImg: string; + + constructor(private platform: Platform, private statusBar: StatusBar, private splashScreen: SplashScreen, private rootScope: GlobalScopeService) { + this.initializeApp(); } + private affiliationCount: number = 0; 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. - this.statusBar.styleDefault(); - this.splashScreen.hide(); + //this.statusBar.styleBlackTranslucent(); + this.statusBar.backgroundColorByHexString('#222'); + + // If using Crosswalk add to index (or change color) + // Ref.: https://forum.ionicframework.com/t/ionic2-rc0-and-ionic2-rc1-plugin-statusbar-not-working-in-android-with-crosswalk/66449/24 + + // http://stackoverflow.com/questions/41544016/white-screen-after-splashscreen-ionic2-android-device + // Ref.: http://www.codingandclimbing.co.uk/blog/ionic-2-fix-splash-screen-white-screen-issue/ + setTimeout(() => { + this.splashScreen.hide(); + }, 10); + + this.nav.swipeBackEnabled = false; + + this.isJedi = this.rootScope.getItem('isJedi'); + try { + // jedi, sith, none + this.affiliation = this.rootScope.getItem('affiliation'); + switch(this.affiliation) { + case 'jedi': + this.isJedi = true; + this.affiliation = 'jedi'; + this.affiliationImg = 'kenobi'; + break; + case 'sith': + this.isJedi = false; + this.affiliation = 'sith'; + this.affiliationImg = 'vader'; + break; + case 'none': + this.isJedi = undefined; + this.affiliation = 'none'; + this.affiliationImg = 'boba'; + break; + default: + console.log('No affiliation. Defaulting value'); + this.isJedi = undefined; + this.affiliation = 'none'; + this.affiliationImg = 'boba'; + } + + } catch(err) { + console.error('ERROR, Could not retrieve affiliation', err); + this.isJedi = undefined; + this.affiliation = 'none'; + this.affiliationImg = 'boba'; + } + + if(this.isJedi !== undefined){ + this.rootScope.setItem('isJedi', this.isJedi); + } else { + this.rootScope.clearItem('isJedi'); + } + this.rootScope.setItem('affiliation', this.affiliation); + this.rootScope.setItem('affiliationImg', this.affiliationImg); + + this.isJedi$ = this.rootScope.watch('isJedi').subscribe(variable => { + this.isJedi = (variable != undefined) ? variable.value : undefined; + }); - this.isJedi = false; - this.isSith = false; }); } openPage(page) { - this.nav.push(page); + this.nav.setRoot(page); } changeAffiliation() { - console.log("Change affiliation!"); - } + + this.affiliationCount++; + switch (this.affiliationCount){ + case 1: + this.affiliationImg = 'kenobi'; + this.affiliation = 'jedi'; + this.isJedi = true; + break; + case 2: + this.affiliationImg = 'vader'; + this.affiliation = 'sith'; + this.isJedi = false; + break; + default: + this.affiliationImg = 'boba'; + this.affiliation = 'none'; + this.isJedi = undefined; + this.affiliationCount = 0; + } + + this.rootScope.setItem('isJedi', this.isJedi); + this.rootScope.setItem('affiliation', this.affiliation); + this.rootScope.setItem('affiliationImg', this.affiliationImg); - chewieMode() { - console.log('Chewie mode'); + } exitApp() { diff --git a/src/app/app.html b/src/app/app.html index f41b4c7..08d74f9 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -8,19 +8,25 @@ - + - - - -

No afiliation

- -
+ + + +

{{ (isJedi === undefined) ? 'No affiliation':(isJedi ? 'Jedi' : 'Sith')}}

+

(click to change)

+ - Enjoy the Splash (again) - Random joke - Credits - About + + + +
diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ad22667..cbc3553 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,10 +8,14 @@ import { StatusBar } from '@ionic-native/status-bar'; import { MyApp } from './app.component'; import { SwJokeProvider } from '../providers/sw-joke/sw-joke'; import { SwApiServiceProvider } from '../providers/sw-api-service/sw-api-service'; +import { GlobalScopeService } from '../providers/global-scope-service/global-scope-service'; +import { SpyDirective } from '../directives/spy/spy'; +import { ShyriiwookPipe } from '../pipes/shyriiwook/shyriiwook'; @NgModule({ declarations: [ MyApp, + SpyDirective, ], imports: [ BrowserModule, @@ -27,7 +31,9 @@ import { SwApiServiceProvider } from '../providers/sw-api-service/sw-api-service SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, SwJokeProvider, - SwApiServiceProvider + SwApiServiceProvider, + GlobalScopeService + ] }) export class AppModule {} diff --git a/src/app/app.scss b/src/app/app.scss index 9d22a1c..d399fee 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -1,6 +1,4 @@ // http://ionicframework.com/docs/v2/theming/ - - // App Global Sass // -------------------------------------------------- // Put style rules here that you want to apply globally. These @@ -14,55 +12,71 @@ // To declare rules for a specific mode, create a child rule // for the .md, .ios, or .wp mode classes. The mode class is // automatically applied to the element in the app. - -custom-header ~ ion-content.content-md { - .fixed-content, .scroll-content { - margin-top: 112px; - } +custom-header~ion-content.content-md { + .fixed-content, + .scroll-content { + margin-top: 94px; + } } -custom-header ~ ion-content.content-ios { - .fixed-content, .scroll-content { - margin-top: 104px; - } + +custom-header~ion-content.content-ios { + .fixed-content, + .scroll-content { + margin-top: 105px; + } } .sith { - color: red!important; + color: red!important; } + .jedi { - color: rgb(75, 213, 238)!important; + color: rgb(75, 213, 238)!important; } + .sw { - color: #ffd54e!important; + color: #ffd54e!important; } ion-menu { - ion-content.content-ios, ion-content.content-md { - background-color: #333; - } - - ion-list ion-item.item-ios, ion-list ion-item.item-md { - background-color: rgba(85, 85, 85, 0.1); - color: inherit; - } - ion-list ion-label h2 { - color: inherit; - } - - ion-avatar img { - margin-left: 1em; - margin-right: 0; - height: 50px!important; - width: 50px!important; - } + ion-list-header.list-header-md { + background-color: #333; + } + ion-content.content-ios, + ion-content.content-md { + background-color: #333; + } + ion-list button.item-ios, + ion-list button.item-md { + background-color: rgba(85, 85, 85, 0.1); + color: inherit; + } + ion-list button.item-ios.activated, + ion-list button.item-md.activated { + background-color: rgba(85, 85, 85, 0.5); + } + ion-list ion-label h2 { + color: inherit; + } + ion-avatar img { + margin-left: 1em; + margin-right: 0; + height: 50px!important; + width: 50px!important; + } + ion-footer.footer.footer-ios { + display: none; + } } ion-nav { - .content-ios, .content-md { - background: #304352; /* fallback for old browsers */ -background: -webkit-linear-gradient(to top, #d7d2cc, #1b2735); /* Chrome 10-25, Safari 5.1-6 */ -background: linear-gradient(to top, #d7d2cc, #1b2735); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ - + .content-ios, + .content-md { + background: #304352; + /* fallback for old browsers */ + background: -webkit-linear-gradient(to top, #d7d2cc, #1b2735); + /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to top, #d7d2cc, #1b2735); + /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + } } -} - diff --git a/src/components/custom-header/custom-header.html b/src/components/custom-header/custom-header.html index 350d185..7e4a0b6 100644 --- a/src/components/custom-header/custom-header.html +++ b/src/components/custom-header/custom-header.html @@ -1,29 +1,34 @@ - - + + -

{{appName}}

+

{{appName}}

- - - - - - + + +
- - - + - {{pageTitle}} + + {{pageTitle}} - +
diff --git a/src/components/custom-header/custom-header.module.ts b/src/components/custom-header/custom-header.module.ts index ff6cb85..121d0f9 100644 --- a/src/components/custom-header/custom-header.module.ts +++ b/src/components/custom-header/custom-header.module.ts @@ -1,16 +1,18 @@ import { NgModule } from '@angular/core'; import { IonicModule } from 'ionic-angular'; -import { CustomHeader } from './custom-header'; +import { SpyDirective } from "../../directives/spy/spy"; +//import { CustomHeader } from './custom-header'; @NgModule({ declarations: [ - CustomHeader, + //CustomHeader, ], imports: [ IonicModule, + SpyDirective ], exports: [ - CustomHeader + //CustomHeader ] }) export class CustomHeaderModule {} diff --git a/src/components/custom-header/custom-header.ts b/src/components/custom-header/custom-header.ts index 2ec96d3..588c82a 100644 --- a/src/components/custom-header/custom-header.ts +++ b/src/components/custom-header/custom-header.ts @@ -1,5 +1,9 @@ -import { Component, Input } from '@angular/core'; -import { AlertController, Toggle } from "ionic-angular"; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { AlertController, Toggle, NavController, Events } from "ionic-angular"; +import { GlobalScopeService } from "../../providers/global-scope-service/global-scope-service"; +import { Subscription } from "rxjs/Subscription"; +import { Subject } from "rxjs/Subject"; +import 'rxjs/add/operator/takeUntil'; /** * Generated class for the CustomHeader component. @@ -11,41 +15,118 @@ import { AlertController, Toggle } from "ionic-angular"; selector: 'custom-header', templateUrl: 'custom-header.html' }) -export class CustomHeader { +export class CustomHeader implements OnInit, OnDestroy { + + private ngUnsubscribe: Subject = new Subject(); @Input('appName') appName: string; @Input('pageTitle') pageTitle: string; + + chewie : boolean = false; + isJedi : boolean; + isJedi$: Subscription; + chewie$: Subscription; + + constructor(private nav: NavController, private alertCtrl: AlertController, private rootScope: GlobalScopeService, + private events: Events) { + console.log('customHeader constructor'); + } + + ngOnInit(): void { + console.log('customHeader ngInit'); + //this.initializeEventHandlers(); - chewie: boolean = false; + this.isJedi = this.rootScope.getItem('isJedi'); + if (this.isJedi !== undefined) { + this.rootScope.setItem('isJedi', this.isJedi); + } else { + this.rootScope.clearItem('isJedi'); + } + + this.chewie = this.rootScope.getItem('chewieMode'); + if(this.rootScope.getItem('chewieMode') === undefined) { + this.chewie = false; + this.rootScope.setItem('chewieMode', this.chewie); + } + + // ref.: http://stackoverflow.com/questions/38008334/angular2-rxjs-when-should-i-unsubscribe-from-subscription + + this.isJedi$ = this.rootScope.watch('isJedi') + .takeUntil(this.ngUnsubscribe) + .subscribe(variable => { + this.isJedi = (variable != undefined) ? variable.value : undefined; + console.log('isJedi watcher ', variable); + }); + + + this.chewie$ = this.rootScope.watch('chewieMode') + .takeUntil(this.ngUnsubscribe) + .subscribe(chewie => { + this.chewie = (chewie != undefined) ? chewie.value : false; + console.log('chewie watcher ', chewie); + }); + + } + ngOnDestroy(): void { + console.log('customHeader ngOnDestroy'); + // this.rootScope.unWatch('isJedi'); + // this.rootScope.unWatch('chewieMode'); - constructor(private alertCtrl: AlertController) { - console.log('Hello CustomHeader Component'); + this.isJedi$.unsubscribe(); + this.chewie$.unsubscribe(); + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); } - chewieMode(toggle: Toggle, chw: any): void { - console.log('chewieMode', chw, toggle); - this.chewie = toggle.checked; - if(toggle.checked) { + chewieMode(chewie: Toggle) { + this.chewie = chewie.value; + this.rootScope.setItem('chewieMode', this.chewie); + if(this.chewie) { this.presentAlert(); } } presentAlert() { - let alert = this.alertCtrl.create({ - title: 'Chewie mode ON', - message: 'Rrrrugh arah-ah-woof?', - buttons: [{ - text: 'OK', - handler: () => { - console.log('OK clicked'); - this.chewie = false; - alert.dismiss(); - - return false; + let alert = this.alertCtrl.create({ + title: 'Chewie mode ON', + message: 'Rrrrugh arah-ah-woof?', + buttons: [{ + text: 'OK', + handler: () => { + alert.dismiss(); + return false; + } + }] + }); + alert.present(); + } + + private alreadyLeft: boolean = false; + private initializeEventHandlers() { + + console.log('Channels: ', this.events['_channels']); + console.log('Event functions', this.events['_channels']['page:didleave']); + + //if(!!this.events['_channels'] && this.events['_channels']['page:didleave'] === undefined){ + if(!this.alreadyLeft){ + this.events.subscribe('page:didleave', () => { + if(!this.alreadyLeft) { + console.log('Captured page:didleave', this); + this.events.unsubscribe('page:didLeave', () => { + console.log('Event unsubscribed'); + this.alreadyLeft = true; + }); + this.isJedi$.unsubscribe(); + this.chewie$.unsubscribe(); + } else { + console.log('Already left, this shouldn\'t happen'); + this.alreadyLeft = true; + } + }); } - }] - }); - alert.present(); -} + + //} + + } } diff --git a/src/index.html b/src/index.html index 4798578..77a36d4 100644 --- a/src/index.html +++ b/src/index.html @@ -2,14 +2,24 @@ - Ionic App + SW Jokes + + + + - + + + + + + + @@ -26,7 +36,7 @@ - + diff --git a/src/pages/about/about.ts b/src/pages/about/about.ts index c5c8d79..806d080 100644 --- a/src/pages/about/about.ts +++ b/src/pages/about/about.ts @@ -1,6 +1,7 @@ import { InAppBrowser } from '@ionic-native/in-app-browser'; import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { IonicPage, NavController, NavParams, Events } from 'ionic-angular'; +import { BasePage } from "../base/base"; /** * Generated class for the AboutPage page. @@ -13,9 +14,14 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular'; selector: 'page-about', templateUrl: 'about.html', }) -export class AboutPage { +export class AboutPage extends BasePage { - constructor(private browser: InAppBrowser, public navCtrl: NavController, public navParams: NavParams) { + constructor(private browser: InAppBrowser, public navCtrl: NavController, public navParams: NavParams, + eventsCtrl: Events) { + // Ref.: http://stackoverflow.com/questions/39836010/ionic-2-page-change-event + // Due to an issue in angular, by now you must send the dependency to the super class + // https://github.com/angular/angular/issues/5155 + super(eventsCtrl); } ionViewDidLoad() { @@ -27,7 +33,7 @@ export class AboutPage { hardwareback: 'yes', location: 'yes' }; - this.browser.create(url, options); + this.browser.create(url, '_self', options); } } diff --git a/src/pages/base/base.ts b/src/pages/base/base.ts new file mode 100644 index 0000000..75d8aa9 --- /dev/null +++ b/src/pages/base/base.ts @@ -0,0 +1,19 @@ +// Ref.: http://stackoverflow.com/questions/39836010/ionic-2-page-change-event +import { Events } from 'ionic-angular'; + +export class BasePage { + + constructor(public eventsCtrl: Events) { } + + ionViewDidEnter() { + this.eventsCtrl.publish('page:load'); + } + + ionViewDidUnload() { + this.eventsCtrl.publish('page:unload'); + } + + ionViewDidLeave() { + this.eventsCtrl.publish('page:didleave'); + } +} diff --git a/src/pages/credits/credits.scss b/src/pages/credits/credits.scss index fee72c3..d46ba7b 100644 --- a/src/pages/credits/credits.scss +++ b/src/pages/credits/credits.scss @@ -1,3 +1,19 @@ page-credits { + ion-content { + ion-list.list-md:not(:first) { + margin-top: 1em; + } + ion-list-header.item-md { + background-color: transparent; + border-top: none; + color: #333; + margin-bottom: 0; + } + + .list-md + ion-list ion-list-header { + margin-top: 0; + } + } + } diff --git a/src/pages/credits/credits.ts b/src/pages/credits/credits.ts index b6c8d28..3f2e5df 100644 --- a/src/pages/credits/credits.ts +++ b/src/pages/credits/credits.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { IonicPage, NavController, NavParams, Events } from 'ionic-angular'; import { InAppBrowser } from "@ionic-native/in-app-browser"; +import { BasePage } from "../base/base"; /** * Generated class for the CreditsPage page. @@ -13,9 +14,11 @@ import { InAppBrowser } from "@ionic-native/in-app-browser"; selector: 'page-credits', templateUrl: 'credits.html', }) -export class CreditsPage { +export class CreditsPage extends BasePage { - constructor(private browser: InAppBrowser, public navCtrl: NavController, public navParams: NavParams) { + constructor(private browser: InAppBrowser, public navCtrl: NavController, public navParams: NavParams, + eventsCtrl: Events) { + super(eventsCtrl); } ionViewDidLoad() { @@ -27,7 +30,7 @@ export class CreditsPage { hardwareback: 'yes', location: 'yes' }; - this.browser.create(url, options); + this.browser.create(url, '_self', options); } } diff --git a/src/pages/joke/joke.html b/src/pages/joke/joke.html index e4cb672..b98ba2a 100644 --- a/src/pages/joke/joke.html +++ b/src/pages/joke/joke.html @@ -9,12 +9,21 @@ -

-

+

- +
diff --git a/src/pages/joke/joke.module.ts b/src/pages/joke/joke.module.ts index dce3d06..4bb5c10 100644 --- a/src/pages/joke/joke.module.ts +++ b/src/pages/joke/joke.module.ts @@ -2,19 +2,18 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { JokePage } from './joke'; import { ComponentsModule } from "../../components/components.modules"; -import { SwJokeProvider } from "../../providers/sw-joke/sw-joke"; +import { ShyriiwookPipe } from "../../pipes/shyriiwook/shyriiwook"; @NgModule({ declarations: [ JokePage, + ShyriiwookPipe ], imports: [ IonicPageModule.forChild(JokePage), ComponentsModule, ], - providers: [ - SwJokeProvider - ], + providers: [], exports: [ JokePage ] diff --git a/src/pages/joke/joke.scss b/src/pages/joke/joke.scss index b488bdd..eef4909 100644 --- a/src/pages/joke/joke.scss +++ b/src/pages/joke/joke.scss @@ -9,6 +9,15 @@ page-joke { width: auto; margin-right: 1em; } + #jokeBtn { + width:90%; + .button-inner { + span { + display: inline-block; + white-space: normal; + } + } + } p { font-style: italic; font-size: 1.5em; @@ -18,4 +27,10 @@ page-joke { p span.quote { font-size: 2em; } + + ion-list-header.item-md { + background-color: transparent; + border-top: none; + color: #444; + } } diff --git a/src/pages/joke/joke.ts b/src/pages/joke/joke.ts index 7865d59..fcf7aa9 100644 --- a/src/pages/joke/joke.ts +++ b/src/pages/joke/joke.ts @@ -1,8 +1,11 @@ import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { IonicPage, NavController, NavParams, Events } from 'ionic-angular'; import { SwJokeProvider } from "../../providers/sw-joke/sw-joke"; import * as _ from 'lodash'; +import { GlobalScopeService } from "../../providers/global-scope-service/global-scope-service"; +import { Subscription } from "rxjs/Subscription"; +import { BasePage } from "../base/base"; /** * Generated class for the JokePage page. @@ -13,36 +16,67 @@ import * as _ from 'lodash'; @IonicPage() @Component({ selector: 'page-joke', - templateUrl: 'joke.html', + templateUrl: 'joke.html' }) -export class JokePage { - public joke: any = {}; +export class JokePage extends BasePage { + joke: any = {}; + isJedi: boolean = false; + isJedi$: Subscription; - constructor(public navCtrl: NavController, public navParams: NavParams, public jokeProvider: SwJokeProvider) { + chewie: boolean = false; + chewie$: Subscription; + + + + variables: any; + + constructor(public navCtrl: NavController, public navParams: NavParams, public jokeProvider: SwJokeProvider, private rootScope: GlobalScopeService, + eventsCtrl: Events) { + + super(eventsCtrl); + + this.isJedi = rootScope.getItem('isJedi'); + this.isJedi$ = rootScope.watch('isJedi').subscribe(variable => { + this.isJedi = (variable != undefined) ? variable.value : undefined; + }); + + this.chewie = this.rootScope.getItem('chewieMode'); + if(this.rootScope.getItem('chewieMode') === undefined) { + this.chewie = false; + this.rootScope.setItem('chewieMode', this.chewie); + } + this.chewie$ = this.rootScope.watch('chewieMode') + .subscribe(chewie => { + this.chewie = (chewie != undefined) ? chewie.value : false; + }); + } - ionViewDidLoad() { - console.log('ionViewDidLoad JokePage'); + ionViewWillLeave() { + //if(!!this.isJedi$){ + this.isJedi$.unsubscribe(); + this.chewie$.unsubscribe(); + //} } - getNewJoke() { - console.log('getNewJoke'); + ionViewDidLoad() {} + + getNewJoke() { this.joke = this.jokeProvider.getJoke(); } - ionViewWillEnter(): void { - console.log('ionViewWillEnter JokePage', this.navParams); - if(!!this.navParams.data && this.navParams.data.back){ - console.log(this.joke); - if(!!!this.navParams.data.joke.joke) { - console.log('Loading new joke, from back button.'); + ionViewWillEnter(): void { + if(!!this.navParams.data && this.navParams.data.back){ + if(!!!this.navParams.data.joke.joke) { this.getNewJoke(); } else { - this.joke = this.navParams.data.joke; - console.log('Do nothing. Back from Joke annotations.'); + this.joke = this.navParams.data.joke; } - } else { - console.log('Loading new joke'); + } else { + this.getNewJoke(); + } + + if(!!!this.joke){ this.getNewJoke(); } @@ -50,7 +84,7 @@ export class JokePage { loadMoreInfo(refData: any) { _.assign(refData, { joke: this.joke }); - this.navCtrl.push('MoreInfoPage', refData); + this.navCtrl.setRoot('MoreInfoPage', refData); } } diff --git a/src/pages/more-info/more-info.scss b/src/pages/more-info/more-info.scss index a4557ed..8035519 100644 --- a/src/pages/more-info/more-info.scss +++ b/src/pages/more-info/more-info.scss @@ -14,7 +14,7 @@ page-more-info { } ion-col h2 { padding-top: 0.9em; - color: #fff; + color: #cecece; } padding: 0; } @@ -26,4 +26,13 @@ page-more-info { padding-left: 1.5em; } + ion-list-header.list-header.item-md { + background-color: transparent; + margin-bottom: 0; + } + + ion-list.list-md { + margin-top: 0; + } + } diff --git a/src/pages/more-info/more-info.ts b/src/pages/more-info/more-info.ts index c1967a1..44afcf8 100644 --- a/src/pages/more-info/more-info.ts +++ b/src/pages/more-info/more-info.ts @@ -1,8 +1,11 @@ import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { IonicPage, NavController, NavParams, Events } from 'ionic-angular'; import { SwApiServiceProvider } from "../../providers/sw-api-service/sw-api-service"; import * as _ from 'lodash'; +import { GlobalScopeService } from "../../providers/global-scope-service/global-scope-service"; +import { Subscription } from "rxjs/Subscription"; +import { BasePage } from "../base/base"; /** * Generated class for the MoreInfoPage page. @@ -15,63 +18,65 @@ import * as _ from 'lodash'; selector: 'page-more-info', templateUrl: 'more-info.html', }) -export class MoreInfoPage { +export class MoreInfoPage extends BasePage { category: string; queryUrl: string; ellapsed: number; response: Array; - constructor(public navCtrl: NavController, public navParams: NavParams, private swApiService: SwApiServiceProvider) { - } + chewie$: Subscription; - ionViewDidLoad() { - console.log('ionViewDidLoad MoreInfoPage'); + constructor(public navCtrl: NavController, public navParams: NavParams, private swApiService: SwApiServiceProvider, private rootScope: GlobalScopeService, + eventsCtrl: Events) { + super(eventsCtrl); } - ionViewWillEnter(): void { - console.log('ionViewWillEnter MoreInfoPage'); - console.log(this.navParams); - - let params = this.navParams.data; - let self: any = this; + ionViewDidLoad() { } - this.category = params.category; - this.queryUrl = params.url; + ionViewWillLeave() { + //if(!!this.chewie$){ + this.chewie$.unsubscribe(); + //} + } + retrieveData(category: string, queryUrl: string) { - this.swApiService.retrieveData(this.category + this.queryUrl).then(res => { - console.log('Query is done ', self.queryUrl, res); + this.swApiService.retrieveData(category + queryUrl).then(res => { this.response = _.map(res['results'], (v, k) => { - console.log('v, k', v, k); - let adjustedData = _.map(_.omit(v, ['films', 'people', 'species', 'starships', 'planets', 'vehicles', 'opening_crawl', 'characters', 'pilots', 'homeworld', 'created', 'edited', 'url']), (vv, kk) => { - console.log('vv, kk', vv, kk); + + let exceptions = ['films', 'people', 'species', 'starships', 'planets', 'vehicles', 'opening_crawl', 'characters', 'pilots', 'homeworld', 'created', 'edited', 'url']; + let shyriiwookExceptions = ['wwahanscc', 'akwoooakanwo', 'cakwooaahwoc', 'caorarccacahakc', 'akanrawhwoaoc', 'howoacahoaanwoc', 'ooakwowhahwhrroarcraohan', 'oaacrarcraoaaoworcc', 'akahanooaoc', 'acooscwoohoorcanwa', 'oarcworaaowowa', 'wowaahaowowa', 'hurcan']; + let adjustedData = _.map(_.omit(v, _.concat([], exceptions, shyriiwookExceptions)), (vv, kk) => { return { label: kk.replace(/_/g, ' '), value: vv }; }); - console.log('adjustedData', adjustedData); + return { data: adjustedData }; - }); - - console.log('Adjusted response data: ', this.response) + }); }); - //Store data - //Get keys from data - //Adjust key text (replace _) - // Iterate over response using only the keys - // or iterate over an adjusted arrray + } + + ionViewWillEnter(): void { + let params = this.navParams.data; + + this.category = params.category; + this.queryUrl = params.url; + + //this.retrieveData(this.category, this.queryUrl); + this.chewie$ = this.rootScope.watch('chewieMode').subscribe(chewie => { + this.retrieveData(this.category, this.queryUrl); + }); } goBack() { - this.navCtrl.push('JokePage', { back: true, joke: this.navParams.data.joke }); + this.navCtrl.setRoot('JokePage', { back: true, joke: this.navParams.data.joke }); } - - } diff --git a/src/pages/splash/splash.ts b/src/pages/splash/splash.ts index 61feb2d..0d85292 100644 --- a/src/pages/splash/splash.ts +++ b/src/pages/splash/splash.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { IonicPage, NavController, NavParams, Events } from 'ionic-angular'; +import { BasePage } from "../base/base"; /** * Generated class for the SplashPage page. @@ -12,13 +13,17 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular'; selector: 'page-splash', templateUrl: 'splash.html', }) -export class SplashPage { +export class SplashPage extends BasePage { public showBtn: boolean = false; - constructor(public navCtrl: NavController, public navParams: NavParams) {} + constructor(public navCtrl: NavController, public navParams: NavParams, eventsCtrl: Events) { + super(eventsCtrl); + } + + showHideBtn() { this.showBtn = false; diff --git a/src/pipes/shyriiwook/shyriiwook.ts b/src/pipes/shyriiwook/shyriiwook.ts new file mode 100644 index 0000000..87a3e52 --- /dev/null +++ b/src/pipes/shyriiwook/shyriiwook.ts @@ -0,0 +1,46 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import * as _ from 'lodash'; + +/** + * Generated class for the ShyriiwookPipe pipe. + * + * See https://angular.io/docs/ts/latest/guide/pipes.html for more info on + * Angular Pipes. + */ +@Pipe({ + name: 'shyriiwook', + pure: false +}) +export class ShyriiwookPipe implements PipeTransform { + + // Ref.: https://github.com/dmke/wookie-translator/blob/master/lib/wookie/dialects/simple.rb + static wookieMap:any = { + 'a' : 'ra', 'b' : 'rh', 'c' : 'oa', 'd' : 'wa', 'e' : 'wo', 'f' : 'ww', + 'g' : 'rr', 'h' : 'ac', 'i' : 'ah', 'j' : 'sh', 'k' : 'or', 'l' : 'an', + 'm' : 'sc', 'n' : 'wh', 'o' : 'oo', 'p' : 'ak', 'q' : 'rq', 'r' : 'rc', + 's' : 'c', 't' : 'ao', 'u' : 'hu', 'v' : 'ho', 'w' : 'oh', 'x' : 'k', + 'y' : 'ro', 'z' : 'uf' + }; + + private toShyriiwook(text: string): string { + let translatedText: string = text; + let translatedTextArr: Array = _.map(text.split(''), (v: string) => { + let origChar: string = ShyriiwookPipe.wookieMap[v.toLowerCase()]; + let tChar: string = (!origChar) ? v : origChar; + //tChar = (!!origChar && v === v.toUpperCase()) ? _.startCase(tChar) : tChar; + // Proper names mantain the original character + tChar = (!!origChar && v === v.toUpperCase()) ? v : tChar; + + return tChar; + }); + + translatedText = translatedTextArr.join(''); + translatedText = translatedText.replace(//g,'
'); + + return translatedText; + } + + transform(value: string, translate?: boolean | false) { + return (!!value && translate) ? this.toShyriiwook(value) : value; + } +} diff --git a/src/providers/global-scope-service/global-scope-service.ts b/src/providers/global-scope-service/global-scope-service.ts new file mode 100644 index 0000000..aefc98d --- /dev/null +++ b/src/providers/global-scope-service/global-scope-service.ts @@ -0,0 +1,94 @@ +import { BehaviorSubject } from "rxjs/BehaviorSubject"; +import { Injectable } from "@angular/core"; +import * as _ from 'lodash'; +import { Subscription } from "rxjs/Subscription"; +import { Observable } from "rxjs/Observable"; +import { Subscriber } from "rxjs/Subscriber"; + +/* + Generated class for the GlobalScopeServiceProvider provider. + + See https://angular.io/docs/ts/latest/guide/dependency-injection.html + for more info on providers and Angular 2 DI. +*/ +// Ref.: https://coryrylan.com/blog/angular-observable-data-services +// Most logic based on http://stackoverflow.com/questions/34434057/angular-2-x-watching-for-variable-change +export interface Data { + key: string, + value: any +} + +@Injectable() +export class GlobalScopeService { + + private keyIndex: any; + private dataArray: Array>; + private dataObservables: Array>; + + constructor() { + this.keyIndex = {}; + this.dataArray = []; + this.dataObservables = []; + } + + // TODO Use Ionic Cache + // Ref. :https://github.com/Nodonisko/ionic-cache + setItem(key: string, data:any) { + let dataObj: Data = { + key: key, + value: data + }; + let idx: number; + + if(_.find(this.dataArray, { value: { key: key } }) === undefined) { + let changeSubject = new BehaviorSubject(dataObj); + this.dataArray.push(changeSubject); + this.dataObservables.push(changeSubject.asObservable()); + + idx = this.dataArray.length - 1; + this.keyIndex[key] = idx; + } else { + idx = this.keyIndex[key]; + } + this.dataArray[idx].next(dataObj); + + } + + getItem(key: string): any { + let idx: number = this.keyIndex[key]; + return (!!this.dataArray[idx]) ? this.dataArray[idx].value.value : undefined; + } + + clearItem(key: string) { + this.setItem(key, undefined); + } + + getAll() { + return _.map(this.dataArray, (v) => { return v.value; }); + } + + watch(variable: string): any { + let idx: number = this.keyIndex[variable]; + try{ + // let watchVar: Observable = this.dataObservables[idx]; + // return watchVar; + return this.dataArray[idx].asObservable(); + } catch(err) { + throw new Error('Variable ' + variable + ' not present. Try setItem("' + variable + '", this.' + variable + ') first.'); + } + + } + + unWatch(variable: string): void { + console.log('unWatch ', variable); + let idx: number = this.keyIndex[variable]; + try{ + let dataValue = this.dataArray[idx].value; + this.dataArray[idx].next(dataValue); + this.dataArray[idx].complete(); + } catch(err) { + throw new Error('Variable ' + variable + ' not present. Try setItem("' + variable + '", this.' + variable + ') first.'); + } + } + +} diff --git a/src/providers/sw-api-service/sw-api-service.ts b/src/providers/sw-api-service/sw-api-service.ts index faf0211..ff182ca 100644 --- a/src/providers/sw-api-service/sw-api-service.ts +++ b/src/providers/sw-api-service/sw-api-service.ts @@ -1,6 +1,8 @@ import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; +import { GlobalScopeService } from "../global-scope-service/global-scope-service"; +import { Subscription } from "rxjs/Subscription"; /* Generated class for the SwApiServiceProvider provider. @@ -12,9 +14,17 @@ import 'rxjs/add/operator/map'; export class SwApiServiceProvider { private baseUrl: string = 'http://swapi.co/api/'; private data: any; + private wookiee: string; + private wookiee$: Subscription; - constructor(public http: Http) { - console.log('Hello SwApiServiceProvider Provider'); + constructor(public http: Http, private rootScope: GlobalScopeService) { + this.wookiee = (this.rootScope.getItem('chewieMode')) ? '?format=wookiee' : ''; + if(this.rootScope.getItem('chewieMode') === undefined) { + this.rootScope.setItem('chewieMode', false); + } + this.wookiee$ = this.rootScope.watch('chewieMode').subscribe(chewie => { + this.wookiee = (!!chewie && chewie.value) ? 'format=wookiee' : ''; + }); } retrieveData(queryUrl) { @@ -28,8 +38,19 @@ export class SwApiServiceProvider { // We're using Angular HTTP provider to request the data, // then on the response, it'll map the JSON data to a parsed JS object. // Next, we process the data and resolve the promise with the new data. - this.http.get(this.baseUrl + queryUrl) - .map(res => res.json()) + let and: string = (this.wookiee !== '') ? (queryUrl.indexOf('?') !== -1) ? '&' : '?' : ''; + this.http.get(this.baseUrl + queryUrl + and + this.wookiee) + .map(res => { + let responseString = res['_body']; + if(responseString.indexOf('whhuanan') !== -1 || responseString.indexOf('rcwochuanaoc') !== -1){ + let newResponse = responseString.replace(/whhuanan/g, 'null'); + newResponse = newResponse.replace(/rcwochuanaoc/g, 'results'); + newResponse = newResponse.replace(/\\/g, ''); + return JSON.parse(newResponse); + } else { + return res.json() + } + }) .subscribe(data => { // we've got back the raw data, now generate the core schedule data // and save the data for later reference diff --git a/src/providers/sw-joke/sw-joke.ts b/src/providers/sw-joke/sw-joke.ts index 33687e1..648c227 100644 --- a/src/providers/sw-joke/sw-joke.ts +++ b/src/providers/sw-joke/sw-joke.ts @@ -20,15 +20,12 @@ export class SwJokeProvider { }> getJoke() { - let rndJoke = _.sample(this.jokes); - console.log(rndJoke); + let rndJoke = _.sample(this.jokes); return rndJoke; } constructor(public http: Http) { - console.log('Hello SwJokeProvider Provider'); - /* {