├── platforms └── .gitignore ├── src ├── pages │ ├── help │ │ ├── help.scss │ │ ├── index.ts │ │ ├── help.module.ts │ │ ├── help.html │ │ └── help.ts │ ├── about │ │ ├── about.scss │ │ ├── index.ts │ │ ├── about.module.ts │ │ ├── about.html │ │ └── about.ts │ ├── login │ │ ├── login.scss │ │ ├── index.ts │ │ ├── login.module.ts │ │ ├── login.html │ │ └── login.ts │ ├── logout │ │ ├── logout.scss │ │ ├── index.ts │ │ ├── logout.module.ts │ │ ├── logout.html │ │ └── logout.ts │ ├── status │ │ ├── status.scss │ │ ├── index.ts │ │ ├── status.module.ts │ │ ├── status.html │ │ └── status.ts │ ├── unlock │ │ ├── unlock.scss │ │ ├── index.ts │ │ ├── unlock.module.ts │ │ ├── unlock.ts │ │ └── unlock.html │ ├── feedback │ │ ├── feedback.scss │ │ ├── index.ts │ │ ├── feedback.module.ts │ │ ├── feedback.ts │ │ └── feedback.html │ ├── setting │ │ ├── setting.scss │ │ ├── index.ts │ │ ├── setting.module.ts │ │ ├── setting.html │ │ └── setting.ts │ ├── dashboard │ │ ├── dashboard.scss │ │ ├── index.ts │ │ ├── dashboard.module.ts │ │ ├── dashboard.html │ │ └── dashboard.ts │ ├── botie-list │ │ ├── botie-list.scss │ │ ├── index.ts │ │ ├── botie-list.module.ts │ │ ├── botie-list.html │ │ └── botie-list.ts │ ├── giftie-list │ │ ├── giftie-list.scss │ │ ├── index.ts │ │ ├── giftie-list.module.ts │ │ ├── giftie-list.html │ │ └── giftie-list.ts │ ├── hostie-edit │ │ ├── hostie-edit.scss │ │ ├── index.ts │ │ ├── hostie-edit.module.ts │ │ ├── hostie-edit.html │ │ └── hostie-edit.ts │ ├── hostie-list │ │ ├── hostie-list.scss │ │ ├── index.ts │ │ ├── hostie-list.module.ts │ │ ├── hostie-list.html │ │ └── hostie-list.ts │ ├── botie-create │ │ ├── botie-create.scss │ │ ├── index.ts │ │ ├── botie-create.module.ts │ │ ├── botie-create.ts │ │ └── botie-create.html │ ├── botie-details │ │ ├── botie-details.scss │ │ ├── index.ts │ │ ├── botie-details.module.ts │ │ ├── botie-details.html │ │ └── botie-details.ts │ ├── hostie-create │ │ ├── hostie-create.scss │ │ ├── index.ts │ │ ├── hostie-create.module.ts │ │ ├── hostie-create.ts │ │ └── hostie-create.html │ ├── hostie-details │ │ ├── hostie-details.scss │ │ ├── index.ts │ │ ├── hostie-details.module.ts │ │ ├── hostie-details.html │ │ └── hostie-details.ts │ └── welcome │ │ ├── index.ts │ │ ├── welcome.scss │ │ ├── welcome.module.ts │ │ ├── welcome.html │ │ └── welcome.ts ├── assets │ ├── imgs │ │ └── logo.png │ └── icon │ │ └── favicon.ico ├── app │ ├── main.ts │ ├── app.html │ ├── app.scss │ ├── app.component.ts │ └── app.module.ts ├── config.ts ├── manifest.json ├── service-worker.js ├── index.html └── theme │ └── variables.scss ├── resources ├── .gitignore ├── icon.png └── README.md ├── chatie.app.json ├── scripts ├── prepare-linux.sh ├── ios.sh ├── prepare-osx.sh ├── deploy-surge.sh ├── sync-version.sh ├── android.sh ├── ios-pro.sh └── pre-push.sh ├── docs └── images │ └── ionic-angular-bot.jpg ├── bs-config.json ├── ionic.config.json ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── .vscode └── settings.json ├── .travis.yml ├── tslint.json ├── package.json ├── config.xml ├── LICENSE └── README.md /platforms/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !platforms.json 3 | -------------------------------------------------------------------------------- /src/pages/help/help.scss: -------------------------------------------------------------------------------- 1 | page-help { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/about/about.scss: -------------------------------------------------------------------------------- 1 | page-about { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/login/login.scss: -------------------------------------------------------------------------------- 1 | page-login { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/logout/logout.scss: -------------------------------------------------------------------------------- 1 | page-logout { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/status/status.scss: -------------------------------------------------------------------------------- 1 | page-status { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/unlock/unlock.scss: -------------------------------------------------------------------------------- 1 | page-unlock { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/feedback/feedback.scss: -------------------------------------------------------------------------------- 1 | page-feedback { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/setting/setting.scss: -------------------------------------------------------------------------------- 1 | page-setting { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.scss: -------------------------------------------------------------------------------- 1 | page-dashboard { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/botie-list/botie-list.scss: -------------------------------------------------------------------------------- 1 | page-botie-list { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/giftie-list/giftie-list.scss: -------------------------------------------------------------------------------- 1 | page-giftie-list { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/hostie-edit/hostie-edit.scss: -------------------------------------------------------------------------------- 1 | page-hostie-edit { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/hostie-list/hostie-list.scss: -------------------------------------------------------------------------------- 1 | page-hostie-list { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /resources/.gitignore: -------------------------------------------------------------------------------- 1 | android/ 2 | ios/ 3 | wp8/ 4 | splash.* 5 | *.md5 6 | -------------------------------------------------------------------------------- /src/pages/botie-create/botie-create.scss: -------------------------------------------------------------------------------- 1 | page-botie-create { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/botie-details/botie-details.scss: -------------------------------------------------------------------------------- 1 | page-botie-details { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/hostie-create/hostie-create.scss: -------------------------------------------------------------------------------- 1 | page-hostie-create { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /chatie.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chatie App", 3 | "baseref": "master" 4 | } 5 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chatie/app/HEAD/resources/icon.png -------------------------------------------------------------------------------- /src/pages/hostie-details/hostie-details.scss: -------------------------------------------------------------------------------- 1 | page-hostie-details { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/about/index.ts: -------------------------------------------------------------------------------- 1 | export * from './about' 2 | export * from './about.module' 3 | -------------------------------------------------------------------------------- /src/pages/help/index.ts: -------------------------------------------------------------------------------- 1 | export * from './help' 2 | export * from './help.module' 3 | -------------------------------------------------------------------------------- /src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login' 2 | export * from './login.module' 3 | -------------------------------------------------------------------------------- /src/pages/logout/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logout' 2 | export * from './logout.module' 3 | -------------------------------------------------------------------------------- /src/pages/setting/index.ts: -------------------------------------------------------------------------------- 1 | export * from './setting' 2 | export * from './setting.module' 3 | -------------------------------------------------------------------------------- /src/pages/status/index.ts: -------------------------------------------------------------------------------- 1 | export * from './status' 2 | export * from './status.module' 3 | -------------------------------------------------------------------------------- /src/pages/unlock/index.ts: -------------------------------------------------------------------------------- 1 | export * from './unlock' 2 | export * from './unlock.module' 3 | -------------------------------------------------------------------------------- /src/pages/welcome/index.ts: -------------------------------------------------------------------------------- 1 | export * from './welcome' 2 | export * from './welcome.module' 3 | -------------------------------------------------------------------------------- /src/assets/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chatie/app/HEAD/src/assets/imgs/logo.png -------------------------------------------------------------------------------- /src/pages/feedback/index.ts: -------------------------------------------------------------------------------- 1 | export * from './feedback' 2 | export * from './feedback.module' 3 | -------------------------------------------------------------------------------- /scripts/prepare-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | echo "pretend to be prepared..." 5 | -------------------------------------------------------------------------------- /src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chatie/app/HEAD/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/pages/botie-list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './botie-list' 2 | export * from './botie-list.module' 3 | -------------------------------------------------------------------------------- /src/pages/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dashboard' 2 | export * from './dashboard.module' 3 | -------------------------------------------------------------------------------- /src/pages/botie-create/index.ts: -------------------------------------------------------------------------------- 1 | export * from './botie-create' 2 | export * from './botie-create.module' 3 | -------------------------------------------------------------------------------- /src/pages/giftie-list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './giftie-list' 2 | export * from './giftie-list.module' 3 | -------------------------------------------------------------------------------- /src/pages/hostie-edit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hostie-edit' 2 | export * from './hostie-edit.module' 3 | -------------------------------------------------------------------------------- /src/pages/hostie-list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hostie-list' 2 | export * from './hostie-list.module' 3 | -------------------------------------------------------------------------------- /docs/images/ionic-angular-bot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chatie/app/HEAD/docs/images/ionic-angular-bot.jpg -------------------------------------------------------------------------------- /src/pages/botie-details/index.ts: -------------------------------------------------------------------------------- 1 | export * from './botie-details' 2 | export * from './botie-details.module' 3 | -------------------------------------------------------------------------------- /src/pages/hostie-create/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hostie-create' 2 | export * from './hostie-create.module' 3 | -------------------------------------------------------------------------------- /src/pages/hostie-details/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hostie-details' 2 | export * from './hostie-details.module' 3 | -------------------------------------------------------------------------------- /src/pages/welcome/welcome.scss: -------------------------------------------------------------------------------- 1 | page-welcome { 2 | ion-slide { 3 | background-color: #32db64; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8100, 3 | "files": ["./www/**/*.{html,htm,css,js}"], 4 | "server": { "baseDir": "./www" } 5 | } 6 | -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chatie", 3 | "app_id": "053e819b", 4 | "type": "ionic-angular", 5 | "integrations": { 6 | "cordova": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scripts/ios.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | ionic cordova build ios \ 5 | --aot \ 6 | --minifyjs \ 7 | --minifycss \ 8 | --optimizejs \ 9 | --prod \ 10 | --release 11 | -------------------------------------------------------------------------------- /scripts/prepare-osx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | brew update 5 | brew cleanup 6 | brew cask cleanup 7 | 8 | brew install \ 9 | moreutils \ 10 | jq \ 11 | shellcheck 12 | -------------------------------------------------------------------------------- /src/app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' 2 | 3 | import { AppModule } from './app.module' 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule) 6 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | const pkg = require('../package.json') 2 | export const VERSION: string = pkg.version 3 | 4 | /** 5 | * Auth0 API Configuration 6 | */ 7 | export const AUTH0_SETTINGS = { 8 | CLIENT_ID: 'kW2jmKVAO6xMY9H4fYPUtFJSSRJbe3sz', 9 | DOMAIN: 'zixia.auth0.com', 10 | } 11 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chatie", 3 | "short_name": "Chatie", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "icons": [{ 7 | "src": "assets/imgs/logo.png", 8 | "sizes": "512x512", 9 | "type": "image/png" 10 | }], 11 | "background_color": "#4e8ef7", 12 | "theme_color": "#4e8ef7" 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/help/help.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { HelpPage } from './help' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | HelpPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(HelpPage), 11 | ], 12 | }) 13 | export class HelpPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/login/login.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { LoginPage } from './login' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | LoginPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(LoginPage), 11 | ], 12 | }) 13 | export class LoginPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/logout/logout.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { LogoutPage } from './logout' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | LogoutPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(LogoutPage), 11 | ], 12 | }) 13 | export class LogoutPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/unlock/unlock.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { UnlockPage } from './unlock' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | UnlockPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(UnlockPage), 11 | ], 12 | }) 13 | export class UnlockPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/about/about.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { AboutPage } from './about' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | AboutPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(AboutPage), 11 | ], 12 | }) 13 | export class AboutPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/setting/setting.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { SettingPage } from './setting' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | SettingPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(SettingPage), 11 | ], 12 | }) 13 | export class SettingPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/welcome/welcome.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { WelcomePage } from './welcome' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | WelcomePage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(WelcomePage), 11 | ], 12 | }) 13 | export class WelcomePageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/feedback/feedback.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { FeedbackPage } from './feedback' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | FeedbackPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(FeedbackPage), 11 | ], 12 | }) 13 | export class FeedbackPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/status/status.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { StatusPage } from './status' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | StatusPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(StatusPage), 11 | ], 12 | }) 13 | export class StatusPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/botie-list/botie-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { BotieListPage } from './botie-list' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | BotieListPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(BotieListPage), 11 | ], 12 | }) 13 | export class BotieListPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { DashboardPage } from './dashboard' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | DashboardPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(DashboardPage), 11 | ], 12 | }) 13 | export class DashboardPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/giftie-list/giftie-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { GiftieListPage } from './giftie-list' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | GiftieListPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(GiftieListPage), 11 | ], 12 | }) 13 | export class GiftieListPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/hostie-edit/hostie-edit.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { HostieEditPage } from './hostie-edit' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | HostieEditPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(HostieEditPage), 11 | ], 12 | }) 13 | export class HostieEditPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/hostie-list/hostie-list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { HostieListPage } from './hostie-list' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | HostieListPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(HostieListPage), 11 | ], 12 | }) 13 | export class HostieListPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/botie-create/botie-create.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { BotieCreatePage } from './botie-create' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | BotieCreatePage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(BotieCreatePage), 11 | ], 12 | }) 13 | export class BotieCreatePageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/hostie-create/hostie-create.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { HostieCreatePage } from './hostie-create' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | HostieCreatePage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(HostieCreatePage), 11 | ], 12 | }) 13 | export class HostieCreatePageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/hostie-details/hostie-details.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | import { HostieDetailsPage } from './hostie-details' 4 | 5 | @NgModule({ 6 | declarations: [ 7 | HostieDetailsPage, 8 | ], 9 | imports: [ 10 | IonicPageModule.forChild(HostieDetailsPage), 11 | ], 12 | }) 13 | export class HostieDetailsPageModule {} 14 | -------------------------------------------------------------------------------- /src/pages/help/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Help 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | Chatie is ChatBot manager in your pocket. 14 |

15 | 16 |

17 | Please learn more from 18 | https://www.chatie.io 19 |

20 | 21 |
22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # We recommend you to keep these unchanged 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | These are Cordova resources. You can replace icon.png and splash.png and run 2 | `ionic cordova resources` to generate custom icons and splash screens for your 3 | app. See `ionic cordova resources --help` for details. 4 | 5 | Cordova reference documentation: 6 | 7 | - Icons: https://cordova.apache.org/docs/en/latest/config_ref/images.html 8 | - Splash Screens: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/ 9 | -------------------------------------------------------------------------------- /scripts/deploy-surge.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | npm version 5 | 6 | npm run build:browser:prod 7 | 8 | echo app.chatie.io | tee www/CNAME 9 | 10 | cp -v resources/icon.png www/assets/imgs/logo.png 11 | 12 | # 13 | # NOTICE: we keep the domain chatie.surge.sh just for the CNAME alias of app.chatie.io. 14 | # chatie.surge.sh is a totally different site from app.chatie.io, 15 | # which means we should keep those two sites on surge.sh. 16 | # 17 | surge www/ app.chatie.io 18 | -------------------------------------------------------------------------------- /src/pages/botie-details/botie-details.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core' 2 | import { IonicPageModule } from 'ionic-angular' 3 | 4 | import { QRCodeModule } from 'angularx-qrcode' 5 | import { WechatyModule } from '@chatie/angular' 6 | 7 | import { BotieDetailsPage } from './botie-details' 8 | 9 | @NgModule({ 10 | declarations: [ 11 | BotieDetailsPage, 12 | ], 13 | imports: [ 14 | IonicPageModule.forChild(BotieDetailsPage), 15 | QRCodeModule, 16 | WechatyModule, 17 | ], 18 | }) 19 | export class BotieDetailsPageModule {} 20 | -------------------------------------------------------------------------------- /src/pages/welcome/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

slide1

5 | 6 |
7 | 8 | 9 |

slide2

10 |
11 | 12 | 13 |

slide3

14 |
15 | 16 | 17 | 18 | 19 |

slide4/row

20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /src/pages/status/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Status 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Web Socket API 16 | 17 | 18 | 19 | 20 | REST API 21 | 22 | 23 | 24 | 25 | Database 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/pages/help/help.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import { Brolog } from 'brolog' 9 | 10 | @IonicPage() 11 | @Component({ 12 | selector: 'page-help', 13 | templateUrl: 'help.html', 14 | }) 15 | export class HelpPage { 16 | 17 | constructor( 18 | public log: Brolog, 19 | public navCtrl: NavController, 20 | public navParams: NavParams, 21 | ) { 22 | this.log.verbose('HelpPage', 'constructor()') 23 | } 24 | 25 | public ionViewDidLoad() { 26 | this.log.verbose('HelpPage', 'ionViewDidLoad()') 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Menu 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/pages/status/status.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import { Brolog } from 'brolog' 9 | 10 | @IonicPage() 11 | @Component({ 12 | selector: 'page-status', 13 | templateUrl: 'status.html', 14 | }) 15 | export class StatusPage { 16 | 17 | constructor( 18 | public log: Brolog, 19 | public navCtrl: NavController, 20 | public navParams: NavParams, 21 | ) { 22 | this.log.verbose('StatusPage', 'constructor()') 23 | } 24 | 25 | public ionViewDidLoad() { 26 | this.log.verbose('StatusPage', 'ionViewDidLoad()') 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/feedback/feedback.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import { Brolog } from 'brolog' 9 | 10 | @IonicPage() 11 | @Component({ 12 | selector: 'page-feedback', 13 | templateUrl: 'feedback.html', 14 | }) 15 | export class FeedbackPage { 16 | 17 | constructor( 18 | public log: Brolog, 19 | public navCtrl: NavController, 20 | public navParams: NavParams, 21 | ) { 22 | this.log.verbose('FeedbackPage', 'constructor()') 23 | } 24 | 25 | public ionViewDidLoad() { 26 | this.log.verbose('FeedbackPage', 'ionViewDidLoad()') 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/logout/logout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Logout 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

{{ (auth.profile | async).nickname }}

18 |

{{ (auth.profile | async).email }}

19 |
20 |
21 | 22 | 29 | 30 |
31 |     {{ auth.profile | async | json }}
32 |   
33 | 34 |
35 | -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- 1 | // http://ionicframework.com/docs/theming/ 2 | 3 | 4 | // App Global Sass 5 | // -------------------------------------------------- 6 | // Put style rules here that you want to apply globally. These 7 | // styles are for the entire app and not just one component. 8 | // Additionally, this file can be also used as an entry point 9 | // to import other Sass files to be included in the output CSS. 10 | // 11 | // Shared Sass variables, which can be used to adjust Ionic's 12 | // default Sass variables, belong in "theme/variables.scss". 13 | // 14 | // To declare rules for a specific mode, create a child rule 15 | // for the .md, .ios, or .wp mode classes. The mode class is 16 | // automatically applied to the element in the app. 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | *.log 7 | *.tmp 8 | *.tmp.* 9 | log.txt 10 | *.sublime-project 11 | *.sublime-workspace 12 | npm-debug.log* 13 | 14 | .idea/ 15 | .sourcemaps/ 16 | .sass-cache/ 17 | .tmp/ 18 | .versions/ 19 | coverage/ 20 | dist/ 21 | node_modules/ 22 | tmp/ 23 | temp/ 24 | hooks/ 25 | platforms/ 26 | plugins/ 27 | plugins/android.json 28 | plugins/ios.json 29 | www/ 30 | $RECYCLE.BIN/ 31 | 32 | .DS_Store 33 | Thumbs.db 34 | UserInterfaceState.xcuserstate 35 | yarn.lock 36 | t.* 37 | www.zip 38 | .io-config.json 39 | *.bak 40 | *.keystore 41 | project.zip 42 | Chatie*.apk 43 | Chatie*.ipa 44 | package-lock.json 45 | /res/ 46 | -------------------------------------------------------------------------------- /scripts/sync-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION=$(jq -r .version < package.json) 5 | 6 | MAJOR_MINOR=${VERSION%.*} 7 | PATCH=${VERSION##*.} 8 | PATCH_NEXT=$((PATCH+1)) 9 | 10 | VERSION_NEXT="$MAJOR_MINOR.$PATCH_NEXT" 11 | 12 | # 13 | # Sync the App version to package.json 14 | # 15 | sed -i.bak \ 16 | '/"io.chatie.app" version="/s/version="[^"]*"/version="'"$VERSION_NEXT"'"/' \ 17 | config.xml 18 | 19 | rm -f config.xml.bak 20 | 21 | MSG="bumping $VERSION -> $VERSION_NEXT" 22 | echo 23 | echo "$MSG" 24 | echo 25 | 26 | git add config.xml 27 | 28 | if git status | grep "nothing to commit" > /dev/null 2>&1; then 29 | echo 'Clean repository - nothing to commit.' 30 | else 31 | echo 'Prepareing to commit...' 32 | git commit -m "$MSG" 33 | fi 34 | -------------------------------------------------------------------------------- /src/pages/feedback/feedback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | Feedback 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | Join Wechaty Developers' Community 17 |

18 | 19 |

20 | Wechaty is used in many ChatBot projects by hundreds of developers. If you want to talk with other developers, just scan the following QR Code in WeChat with secret code **wechaty**, you can join our Wechaty Developers' Home at once. 21 |

22 | 23 | 24 | 25 | 26 | Scan now, because other Wechaty developers want to talk with you too! (secret code: _wechaty_) 27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check out https://googlechromelabs.github.io/sw-toolbox/ for 3 | * more info on how to use sw-toolbox to custom configure your service worker. 4 | */ 5 | 6 | 7 | 'use strict'; 8 | importScripts('./build/sw-toolbox.js'); 9 | 10 | self.toolbox.options.cache = { 11 | name: 'ionic-cache' 12 | }; 13 | 14 | // pre-cache our key assets 15 | self.toolbox.precache( 16 | [ 17 | './build/main.js', 18 | './build/vendor.js', 19 | './build/main.css', 20 | './build/polyfills.js', 21 | 'index.html', 22 | 'manifest.json' 23 | ] 24 | ); 25 | 26 | // dynamically cache any other local assets 27 | self.toolbox.router.any('/*', self.toolbox.fastest); 28 | 29 | // for any other requests go to the network, cache, 30 | // and then only use that cached resource if your user goes offline 31 | self.toolbox.router.default = self.toolbox.networkFirst; 32 | -------------------------------------------------------------------------------- /scripts/android.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION=$(jq -r .version < package.json) 5 | 6 | KEYSTORE='akamobi.keystore' 7 | [ -e "$KEYSTORE" ] || { 8 | >&2 echo "ERROR: Keystore $KEYSTORE not found." 9 | exit 1 10 | } 11 | 12 | APK_RELEASE_UNSIGNED='platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk' 13 | APK_RELEASE_SIGNED="Chatie-$VERSION.apk" 14 | 15 | # rm before re-build 16 | rm -f "$APK_RELEASE_UNSIGNED" 17 | ionic cordova build android --release 18 | 19 | jarsigner -verbose \ 20 | -sigalg SHA1withRSA \ 21 | -digestalg SHA1 \ 22 | -keystore "$KEYSTORE" \ 23 | -storepass "$ANDROID_SDK_KEYSTORE_PASSWORD_CHATIE" \ 24 | "$APK_RELEASE_UNSIGNED" \ 25 | release 26 | 27 | # rm before re-generate 28 | rm -f "$APK_RELEASE_SIGNED" 29 | /usr/lib/android-sdk/build-tools/*/zipalign -v 4 \ 30 | "$APK_RELEASE_UNSIGNED" \ 31 | "$APK_RELEASE_SIGNED" \ 32 | -------------------------------------------------------------------------------- /scripts/ios-pro.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | figlet 'ionic package build ios' 5 | buildId=$(ionic package build ios --release --profile prod | grep 'Build ID:' | awk '{print $3}') || buildId=-1 6 | 7 | if [ "$buildId" == "-1" ]; then 8 | >&2 echo 'ERROR: ionic build failed' 9 | fi 10 | 11 | ttl=10 12 | figlet "ionic package info $buildId" 13 | status=$(ionic package info "$buildId" | grep status | awk '{print $3}') 14 | while [ "$status" != "SUCCESS" -a "$ttl" -gt 0 ]; do 15 | sleep 10 16 | ((ttl--)) 17 | echo "ionic package info $buildId: status=$status, ttl=$ttl" 18 | status=$(ionic package info "$buildId" | grep status | awk '{print $3}') 19 | done 20 | 21 | figlet "ionic package download $buildId" 22 | ionic package download "$buildId" 23 | 24 | version=$(jq -r .version < package.json) 25 | # mv "Chatie-$buildId.ipa" "Chatie-$version.ipa" 26 | mv "Chatie.ipa" "Chatie-$version.ipa" 27 | 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "noUnusedLocals": true 8 | , "noImplicitReturns": true 9 | , "noFallthroughCasesInSwitch": true 10 | , "strictNullChecks": true 11 | , "alwaysStrict": true 12 | , "noImplicitAny": false 13 | , "noImplicitThis": true, 14 | "lib": [ 15 | "dom", 16 | "es2015", 17 | "esnext" 18 | ], 19 | "module": "es2015", 20 | "moduleResolution": "node", 21 | "sourceMap": true, 22 | "target": "es6" 23 | }, 24 | "include": [ 25 | "src/**/*.ts" 26 | ], 27 | "exclude": [ 28 | "node_modules", 29 | "src/**/*.spec.ts", 30 | "src/**/__tests__/*.ts" 31 | ], 32 | "compileOnSave": false, 33 | "atom": { 34 | "rewriteTsconfig": false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/hostie-edit/hostie-edit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hostie Edit 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Name 26 | 30 | 31 | 32 | 33 | Note 34 | 38 | 39 | 40 | 41 | 42 | 43 | TOKEN: {{ hostie.token }} 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/pages/login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |

23 | Currently we are in Alpha stage, login will only support login via Github. 24 | If you want to join Chatie, please login by authorize your Github account. 25 |

26 | 27 |
28 | 37 |
38 | 39 | 40 | 41 | 42 | Chatie v{{ version }} 43 | 44 | 45 | 46 | 47 |
48 | -------------------------------------------------------------------------------- /src/pages/hostie-details/hostie-details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hostie Details 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

17 | 18 | {{ hostie.name }} 19 |

20 | 21 | 22 | {{ hostie.note }} 23 | 24 | 25 | 26 |
27 |

Hostie environment:

28 | 29 | Wechaty v{{ hostie.version }} 30 | 31 | 32 |
33 |
34 | 35 | 36 | 37 | 40 | 41 | {{ hostie.token }} 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | "editor.tabSize": 2, 5 | "window.zoomLevel": 0, 6 | "editor.minimap.enabled": true, 7 | "files.exclude": { 8 | ".sourcemaps/": true 9 | , "node_modules/": true 10 | , "www/": true 11 | , "platforms/": true 12 | , "hooks/": true 13 | , "res/": true 14 | , "resources/": true 15 | , "plugins": true 16 | } 17 | , "alignment": { 18 | "operatorPadding": "right" 19 | , "indentBase": "firstline" 20 | , "surroundSpace": { 21 | "colon": [1, 1], // The first number specify how much space to add to the left, can be negative. The second number is how much space to the right, can be negative. 22 | "assignment": [1, 1], // The same as above. 23 | "arrow": [1, 1], // The same as above. 24 | "comment": 2 // Special how much space to add between the trailing comment and the code. 25 | // If this value is negative, it means don't align the trailing comment. 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/pages/unlock/unlock.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import { Brolog } from 'brolog' 9 | 10 | @IonicPage() 11 | @Component({ 12 | selector: 'page-unlock', 13 | templateUrl: 'unlock.html', 14 | }) 15 | export class UnlockPage { 16 | 17 | constructor( 18 | public log: Brolog, 19 | public navCtrl: NavController, 20 | public navParams: NavParams, 21 | ) { 22 | this.log.verbose('UnlockPage', 'constructor()') 23 | } 24 | 25 | public ionViewDidLoad() { 26 | this.log.verbose('UnlockPage', 'ionViewDidLoad()') 27 | } 28 | 29 | public securedPing() { 30 | // Here we use authHttp to make an authenticated 31 | // request to the server. Change the endpoint up for 32 | // one that points to your own server. 33 | 34 | // this.authHttp.get('http://localhost:3001/secured/ping') 35 | // .map(res => res.json()) 36 | // .subscribe( 37 | // data => this.log.verbose('UnlockPage', 'securePing() data: %s', data.text), 38 | // err => this.log.verbose('UnlockPage', 'securePing() err: %s', err), 39 | // ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pages/setting/setting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | Setting 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Notification 18 | 21 | 22 | 23 | 29 | 30 | 36 | 37 | 41 | Test Monitoring(Exception) 42 | 43 | 44 |

I'm connected to the {{ deployChannel }}.

45 | 46 |

Download Progress {{ downloadProgress }} / 100

47 | 48 | 49 | Opt in to Beta Features 50 | 51 | 52 | 53 | 57 | Logout 58 | 59 | 60 |
61 | 62 |
 {{ notificate }} 
63 | 64 |
65 | -------------------------------------------------------------------------------- /src/pages/welcome/welcome.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & SPA 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | import { Component } from '@angular/core' 10 | import { 11 | IonicPage, 12 | NavController, 13 | NavParams, 14 | } from 'ionic-angular' 15 | 16 | import { Brolog } from 'brolog' 17 | import { DashboardPage } from '../dashboard/' 18 | // import { LoginPage } from '../login/' 19 | 20 | @IonicPage() 21 | @Component({ 22 | selector: 'page-welcome', 23 | templateUrl: 'welcome.html', 24 | }) 25 | 26 | export class WelcomePage { 27 | 28 | constructor( 29 | public log: Brolog, 30 | public navCtrl: NavController, 31 | public navParams: NavParams, 32 | ) { 33 | this.log.verbose('WelcomePage', 'constructor()') 34 | } 35 | 36 | public ionViewDidLoad() { 37 | this.log.verbose('WelcomePage', 'ionViewDidLoad()') 38 | } 39 | 40 | public async goToDashboard() { 41 | this.log.verbose('WelcomePage', 'goToDashboard()') 42 | 43 | try { 44 | await this.navCtrl.setRoot(DashboardPage) 45 | } catch (e) { 46 | this.log.warn('WelcomePage', 'goToDashboard() exception:%s', e.message) 47 | // await this.navCtrl.push(LoginPage) 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | Dashboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 | 36 | 37 | 38 | 39 | 40 | 41 | News 42 | 43 | 44 | 45 |

Wechaty APP Published, Cheers!

46 |

Thanks Ionic Creator, We can build 47 | Wechaty APP in a week. 48 |

49 |
50 | 51 | 52 | 55 | Read more ... 56 | 57 | 58 | 59 |
60 | 61 |
62 | 63 | -------------------------------------------------------------------------------- /src/pages/unlock/unlock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unlock Pro Features 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

Our goal has always been to create the best free ChatBot manager. To help support the ongoing development of Wechaty, we also offer an optional upgrade with some advanced features that will appreciate.

13 | 14 | 15 | 16 | 17 | 18 | Free 19 | 20 | 21 | Pro 22 | 23 | 24 | 25 | 26 | 27 | Realtime Notification 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Unlimited Hosties 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 55 | 56 | 62 | 63 |
64 | -------------------------------------------------------------------------------- /scripts/pre-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by "git commit" with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, rename this file to "pre-commit". 9 | set -e 10 | 11 | [ -n "$NO_HOOK" ] && exit 0 12 | 13 | [ -n "$GIT_INNER_PRE_HOOK" ] && { 14 | # http://stackoverflow.com/a/21334985/1123955 15 | exit 0 16 | } 17 | 18 | npm run lint 19 | 20 | [ -z "$CYGWIN" ] && { 21 | # git rebase 22 | 23 | scripts/sync-version.sh 24 | 25 | rm -f package-lock.json 26 | npm version patch --no-package-lock 27 | GIT_INNER_PRE_HOOK=1 git push 28 | 29 | cat <<'_STR_' 30 | ____ _ _ ____ _ 31 | / ___(_) |_ | _ \ _ _ ___| |__ 32 | | | _| | __| | |_) | | | / __| '_ \ 33 | | |_| | | |_ | __/| |_| \__ \ | | | 34 | \____|_|\__| |_| \__,_|___/_| |_| 35 | 36 | ____ _ _ 37 | / ___| _ _ ___ ___ ___ ___ __| | | 38 | \___ \| | | |/ __/ __/ _ \/ _ \/ _` | | 39 | ___) | |_| | (_| (_| __/ __/ (_| |_| 40 | |____/ \__,_|\___\___\___|\___|\__,_(_) 41 | 42 | _STR_ 43 | 44 | echo 45 | echo 46 | echo 47 | echo " ### Npm verion bumped and pushed by inner push inside hook pre-push ###" 48 | echo " ------- vvvvvvv outer push will be canceled, never mind vvvvvvv -------" 49 | echo 50 | echo 51 | exit 127 52 | } 53 | 54 | # must run this after the above `test` ([ -z ...]), 55 | # or will whow a error: error: failed to push some refs to 'git@github.com:wechaty/wechaty.git' 56 | echo "PRE-PUSH HOOK PASSED" 57 | echo 58 | -------------------------------------------------------------------------------- /src/pages/botie-list/botie-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | Botie List 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 31 | 32 | 47 | 48 | 49 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Notice: we only support 4 Botie in Alpha stage for testing. 64 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/pages/botie-create/botie-create.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | } from 'ionic-angular' 6 | 7 | import { Brolog } from 'brolog' 8 | import cuid from 'cuid' 9 | 10 | import { 11 | Botie, 12 | BotieStore, 13 | Status, 14 | } from '@chatie/db' 15 | 16 | @IonicPage() 17 | @Component({ 18 | selector: 'page-botie-create', 19 | templateUrl: 'botie-create.html', 20 | }) 21 | export class BotieCreatePage { 22 | private token: string 23 | private name: string 24 | private note: string 25 | 26 | public loading: boolean 27 | 28 | constructor( 29 | public log: Brolog, 30 | public botieStore: BotieStore, 31 | public navCtrl: NavController, 32 | ) { 33 | this.log.verbose('BotieCreatePage', 'constructor()') 34 | 35 | this.token = cuid() 36 | this.name = 'Botie #' + this.token.substr(-2, 2) 37 | this.note = '' 38 | 39 | this.loading = false 40 | } 41 | 42 | public ionViewDidLoad() { 43 | this.log.verbose('BotieCreatePage', 'ionViewDidLoad()') 44 | } 45 | 46 | public async save() { 47 | this.log.verbose('BotieCreatePage', 'save()') 48 | this.loading = true 49 | 50 | const newBotie: Botie = { 51 | token: this.token, 52 | name: this.name, 53 | note: this.note, 54 | status: Status.OFF, 55 | } 56 | 57 | this.log.silly('BotieCreatePage', 'save() newBotie: %s', JSON.stringify(newBotie)) 58 | 59 | this.botieStore.create({ 60 | name: this.name, 61 | token: this.token, 62 | }).then(() => { 63 | this.navCtrl.pop() 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/pages/hostie-list/hostie-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | Hostie List 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 31 | 32 | 47 | 48 | 49 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Notice: we only support 4 Hostie in Alpha stage for testing. 64 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/pages/hostie-create/hostie-create.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | } from 'ionic-angular' 6 | 7 | import { Brolog } from 'brolog' 8 | import cuid from 'cuid' 9 | 10 | import { 11 | Hostie, 12 | HostieStore, 13 | Status, 14 | } from '@chatie/db' 15 | 16 | @IonicPage() 17 | @Component({ 18 | selector: 'page-hostie-create', 19 | templateUrl: 'hostie-create.html', 20 | }) 21 | export class HostieCreatePage { 22 | private token: string 23 | private name: string 24 | private note: string 25 | 26 | public loading: boolean 27 | 28 | constructor( 29 | public log: Brolog, 30 | public hostieStore: HostieStore, 31 | public navCtrl: NavController, 32 | ) { 33 | this.log.verbose('HostieCreatePage', 'constructor()') 34 | 35 | this.token = cuid() 36 | this.name = 'Hostie #' + this.token.substr(-2, 2) 37 | this.note = '' 38 | 39 | this.loading = false 40 | } 41 | 42 | public ionViewDidLoad() { 43 | this.log.verbose('HostieCreatePage', 'ionViewDidLoad()') 44 | } 45 | 46 | public async save() { 47 | this.log.verbose('HostieCreatePage', 'save()') 48 | this.loading = true 49 | 50 | const newHostie: Hostie = { 51 | token: this.token, 52 | name: this.name, 53 | note: this.note, 54 | status: Status.OFF, 55 | } 56 | 57 | this.log.silly('HostieCreatePage', 'save() newHostie: %s', JSON.stringify(newHostie)) 58 | 59 | this.hostieStore.create({ 60 | name: this.name, 61 | token: this.token, 62 | }).then(() => { 63 | this.navCtrl.pop() 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/pages/hostie-create/hostie-create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Hostie 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 29 |

30 | Hostie 31 |

32 |

33 | The place where bot lives in. 34 |

35 |
36 | 37 | 38 | Token 39 | 43 | 44 | 45 | 46 | Name 47 | 51 | 52 | 53 | 54 | Note 55 | 58 | 59 | 60 | 61 | 62 |
63 |

What is a Hostie?

64 | 65 |

66 | Hostie is the home for Botie. In order to make a Botie work, you need a Hostie to run Botie. 67 |

68 | 69 |

More technic:

70 | 71 |
    72 |
  1. 73 | Hostie is a Wechaty Runtime with CPU & Memory & Network resources.
  2. 74 |
  3. 75 | Botie is a group of Functions(Gifties) that you want your bot behavior.
  4. 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | 5 | os: 6 | - linux 7 | - osx 8 | 9 | cache: 10 | directories: 11 | - node_modules 12 | 13 | before_install: 14 | - if [ "$TRAVIS_OS_NAME" == 'linux' ]; then travis_retry scripts/prepare-linux.sh; fi 15 | - if [ "$TRAVIS_OS_NAME" == 'osx' ]; then travis_retry scripts/prepare-osx.sh; fi 16 | 17 | install: travis_retry npm install 18 | 19 | script: 20 | - echo $TRAVIS_OS_NAME 21 | - npm version 22 | - ionic --version 23 | - cordova --version 24 | - ng --version 25 | - npm test 26 | 27 | after_success: 28 | - if [ "$TRAVIS_OS_NAME" == 'linux' ]; then echo 'TODO update coverage'; fi 29 | 30 | stages: 31 | - test 32 | - pack 33 | - name: deploy 34 | if: (type = push) AND branch =~ ^(master|v\d+\.\d+)$ 35 | 36 | jobs: 37 | include: 38 | 39 | - stage: pack 40 | script: 41 | - npm run test:pack && echo 'Npm pack testing is passed' 42 | 43 | - stage: deploy 44 | script: 45 | - echo "Building Web Pages ..." 46 | - npm run build:browser:prod 47 | - echo 'app.chatie.io' | tee www/CNAME 48 | - cp resources/icon.png www/assets/imgs/logo.png 49 | - echo "Deploying to Surge.sh ..." 50 | 51 | deploy: 52 | provider: surge 53 | project: www/ 54 | domain: app.chatie.io 55 | skip_cleanup: true 56 | on: 57 | all_branches: true 58 | 59 | notifications: 60 | webhooks: 61 | urls: 62 | - https://webhooks.gitter.im/e/41a19fbf1d54a04e5217 63 | on_success: always # options: [always|never|change] default: always 64 | on_failure: always # options: [always|never|change] default: always 65 | on_start: never # options: [always|never|change] default: always 66 | email: 67 | on_success: change 68 | on_failure: change 69 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chatie 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/pages/botie-create/botie-create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Botie 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 29 |

30 | Botie 31 |

32 |

33 | The place where bot lives in. 34 |

35 |
36 | 37 | 38 | Token 39 | 43 | 44 | 45 | 46 | Name 47 | 51 | 52 | 53 | 54 | Note 55 | 58 | 59 | 60 | 61 | 62 |
63 |

What is a Botie?

64 | 65 |

66 | A Botie is the configuration for your ChatBot. It's the right place to setup what your bot say when it receives new message, and what Giftie you want to install. 67 |

68 | 69 |

More technic:

70 | 71 |
    72 |
  1. 73 | Botie is like package.json for NPM.
  2. 74 |
  3. 75 | Botie can install many Gifties and put them together as a group. Those Gifties is the Functions that you want your bot behavior.
  4. 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /src/pages/logout/logout.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | } from '@angular/core' 5 | import { 6 | IonicPage, 7 | Loading, 8 | LoadingController, 9 | NavController, 10 | NavParams, 11 | } from 'ionic-angular' 12 | 13 | import { Auth } from 'auth-angular' 14 | import { Brolog } from 'brolog' 15 | 16 | import { LoginPage } from '../login/' 17 | 18 | @IonicPage() 19 | @Component({ 20 | selector: 'page-logout', 21 | templateUrl: 'logout.html', 22 | }) 23 | export class LogoutPage implements OnInit { 24 | public loading?: Loading 25 | 26 | constructor( 27 | public auth: Auth, 28 | public log: Brolog, 29 | public loadingCtrl: LoadingController, 30 | public navCtrl: NavController, 31 | public navParams: NavParams, 32 | ) { 33 | this.log.verbose('LogoutPage', 'constructor()') 34 | } 35 | 36 | public ngOnInit() { 37 | this.log.verbose('LogoutPage', 'ngOnInit()') 38 | 39 | this.auth.valid.first().toPromise().then(valid => { 40 | if (!valid) { 41 | this.logout() 42 | } 43 | }) 44 | } 45 | 46 | public ionViewDidLoad() { 47 | this.log.verbose('LogoutPage', 'ionViewDidLoad()') 48 | } 49 | 50 | public async showLoader(): Promise { 51 | this.log.verbose('LogoutPage', 'showLoader()') 52 | 53 | if (this.loading) { 54 | await this.loading.dismissAll() 55 | } 56 | this.loading = this.loadingCtrl.create({ 57 | content: 'Loading...', 58 | }) 59 | await this.loading.present() 60 | } 61 | 62 | public hideLoader(): void { 63 | this.log.verbose('LogoutPage', 'hideLoader()') 64 | 65 | if (!this.loading) { 66 | return 67 | } 68 | this.loading.dismissAll() 69 | this.loading = undefined 70 | } 71 | 72 | public async logout() { 73 | this.log.verbose('LogoutPage', 'logout()') 74 | 75 | await this.showLoader() 76 | await this.auth.logout() 77 | await this.navCtrl.setRoot(LoginPage) 78 | await this.hideLoader() 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/pages/giftie-list/giftie-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | Giftie List 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | Giftie Management 17 |

18 | 19 | 20 | 21 | 22 | About 23 | 24 | 25 | 26 |

27 | Giftie are extensions of Wechaty functionality written by the community using our plugin development SDK. 28 |

29 |

30 | Are you interested in creating a Wechaty plugin? Check out our SDK and documentation to see exactly what plugins can do, how they work and how easy plugin development can be. 31 |

32 |
33 | 34 | 35 | 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | {{ giftie.title }} 46 | 51 | 52 | 53 | 54 | {{ giftie.description }} 55 | 56 | 57 | 58 | 222 Installs 59 | 18 Stars 60 | 61 | 65 | 66 | 67 | 68 | 69 | 70 |
71 |

Giftie Management is part of our Pro pack.

72 | 73 |

Click below for a full list of all of the features included in out pro pack and to upgrade

74 |
75 | 76 | 85 | 86 |
87 | -------------------------------------------------------------------------------- /src/pages/about/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | About 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | Chatie v{{ version }} 18 | 19 | 20 | 28 | 29 | 37 | 38 | 46 | 47 | 55 | 56 | 64 | 65 | 73 | 74 | 82 | 83 | 91 | 92 | 93 | 94 | 95 | 96 | Copyright © 2017-2018 ♥ 97 | @zixia 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/pages/hostie-edit/hostie-edit.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | LoadingController, 5 | Loading, 6 | NavController, 7 | NavParams, 8 | } from 'ionic-angular' 9 | 10 | import { Brolog } from 'brolog' 11 | 12 | import { 13 | Hostie, 14 | HostieStore, 15 | } from '@chatie/db' 16 | 17 | @IonicPage() 18 | @Component({ 19 | selector: 'page-hostie-edit', 20 | templateUrl: 'hostie-edit.html', 21 | }) 22 | export class HostieEditPage { 23 | public hostie: Hostie 24 | public notify: (newHostie: Hostie) => Promise 25 | 26 | public loading: Loading | null 27 | 28 | constructor( 29 | public hostieStore: HostieStore, 30 | public log: Brolog, 31 | public loadingCtrl: LoadingController, 32 | public navCtrl: NavController, 33 | public navParams: NavParams, 34 | ) { 35 | this.log.verbose('HostieEditPage', 'constructor()') 36 | 37 | // XXX: why Object.assign ??? 38 | this.hostie = Object.assign({}, navParams.get('hostie')) 39 | this.notify = navParams.get('notify') 40 | this.log.silly('HostieEditPage', 'constructor() hostie id:%s', this.hostie.id) 41 | 42 | } 43 | 44 | public ionViewDidLoad() { 45 | this.log.verbose('HostieEditPage', 'ionViewDidLoad()') 46 | } 47 | 48 | public async save() { 49 | this.log.verbose('HostieEditPage', 'save()') 50 | 51 | await this.showLoader() 52 | const ret = await this.hostieStore.update( 53 | this.hostie.id!, 54 | { 55 | name: this.hostie.name, 56 | note: this.hostie.note, 57 | }, 58 | ) 59 | await this.notify(this.hostie) 60 | this.hideLoader() 61 | 62 | this.log.silly('HostieEditPage', 'HostieStore.update() return: %s', JSON.stringify(ret)) 63 | 64 | this.navCtrl.pop() 65 | } 66 | 67 | public showLoader() { 68 | this.log.verbose('HostieEditPage', 'showLoader()') 69 | 70 | this.loading = this.loadingCtrl.create({ 71 | content: 'Loading...', 72 | }) 73 | return this.loading.present() 74 | } 75 | 76 | public hideLoader() { 77 | this.log.verbose('HostieEditPage', 'hideLoader()') 78 | 79 | if (!this.loading) { 80 | return 81 | } 82 | this.loading.dismissAll() 83 | this.loading = null 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/pages/botie-list/botie-list.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | Component, 4 | OnInit, 5 | OnDestroy, 6 | } from '@angular/core' 7 | import { 8 | IonicPage, 9 | NavController, 10 | NavParams, 11 | // reorderArray, 12 | } from 'ionic-angular' 13 | import { 14 | // Observable, 15 | Subscription, 16 | } from 'rxjs/Rx' 17 | 18 | import { Brolog } from 'brolog' 19 | 20 | import { 21 | Botie, 22 | BotieStore, 23 | } from '@chatie/db' 24 | 25 | import { BotieCreatePage } from '../botie-create/' 26 | import { BotieDetailsPage } from '../botie-details/' 27 | 28 | @IonicPage() 29 | @Component({ 30 | selector: 'page-botie-list', 31 | templateUrl: 'botie-list.html', 32 | changeDetection: ChangeDetectionStrategy.OnPush, 33 | }) 34 | export class BotieListPage implements OnInit, OnDestroy { 35 | private botieListSubscription?: Subscription 36 | 37 | public botieList: Botie[] 38 | 39 | constructor( 40 | public botieStore: BotieStore, 41 | public log: Brolog, 42 | public navCtrl: NavController, 43 | public navParams: NavParams, 44 | ) { 45 | this.log.verbose('BotieListPage', 'constructor()') 46 | } 47 | 48 | public ngOnInit() { 49 | this.log.verbose('BotieListPage', 'ngOnInit()') 50 | 51 | this.botieListSubscription = this.botieStore.itemList.subscribe(list => { 52 | this.log.silly('BotieListPage', 'ngOnInit() this.botieStore.itemList.subscript() list.length=%d', list.length) 53 | this.botieList = list 54 | }) 55 | } 56 | 57 | public ngOnDestroy() { 58 | this.log.verbose('BotieListPage', 'ngOnDestroy()') 59 | 60 | if (this.botieListSubscription) { 61 | this.botieListSubscription.unsubscribe() 62 | this.botieListSubscription = undefined 63 | } 64 | } 65 | 66 | public ionViewDidLoad() { 67 | this.log.verbose('BotieListPage', 'ionViewDidLoad()') 68 | } 69 | 70 | public gotoBotieDetail(botie: Botie, event: any) { 71 | this.log.verbose('BotieListPage', 'gotoBotieDetail({id:%s}, %s)', botie.id, event) 72 | 73 | this.navCtrl.push(BotieDetailsPage, { 74 | token: botie.token, 75 | }) 76 | } 77 | 78 | public async trash(botie: Botie): Promise { 79 | this.log.verbose('BotieListPage', 'trash(%s)', botie.id) 80 | if (!botie.id) { 81 | throw new Error('no botie id') 82 | } 83 | await this.botieStore.delete(botie.id) 84 | } 85 | 86 | public add() { 87 | this.navCtrl.push(BotieCreatePage) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 1 | // Ionic Variables and Theming. For more info, please see: 2 | // http://ionicframework.com/docs/theming/ 3 | 4 | // Font path is used to include ionicons, 5 | // roboto, and noto sans fonts 6 | $font-path: "../assets/fonts"; 7 | 8 | 9 | // The app direction is used to include 10 | // rtl styles in your app. For more info, please see: 11 | // http://ionicframework.com/docs/theming/rtl-support/ 12 | $app-direction: ltr; 13 | 14 | 15 | @import "ionic.globals"; 16 | 17 | 18 | // Shared Variables 19 | // -------------------------------------------------- 20 | // To customize the look and feel of this app, you can override 21 | // the Sass variables found in Ionic's source scss files. 22 | // To view all the possible Ionic variables, see: 23 | // http://ionicframework.com/docs/theming/overriding-ionic-variables/ 24 | 25 | 26 | 27 | 28 | // Named Color Variables 29 | // -------------------------------------------------- 30 | // Named colors makes it easy to reuse colors on various components. 31 | // It's highly recommended to change the default colors 32 | // to match your app's branding. Ionic uses a Sass map of 33 | // colors so you can add, rename and remove colors as needed. 34 | // The "primary" color is the only required color in the map. 35 | 36 | $colors: ( 37 | primary: #488aff, 38 | secondary: #32db64, 39 | danger: #f53d3d, 40 | light: #f4f4f4, 41 | dark: #222 42 | ); 43 | 44 | 45 | // App iOS Variables 46 | // -------------------------------------------------- 47 | // iOS only Sass variables can go here 48 | 49 | 50 | 51 | 52 | // App Material Design Variables 53 | // -------------------------------------------------- 54 | // Material Design only Sass variables can go here 55 | 56 | 57 | 58 | 59 | // App Windows Variables 60 | // -------------------------------------------------- 61 | // Windows only Sass variables can go here 62 | 63 | 64 | 65 | 66 | // App Theme 67 | // -------------------------------------------------- 68 | // Ionic apps can have different themes applied, which can 69 | // then be future customized. This import comes last 70 | // so that the above variables are used and Ionic's 71 | // default are overridden. 72 | 73 | @import "ionic.theme.default"; 74 | 75 | 76 | // Ionicons 77 | // -------------------------------------------------- 78 | // The premium icon font for Ionic. For more info, please see: 79 | // http://ionicframework.com/docs/ionicons/ 80 | 81 | @import "ionic.ionicons"; 82 | 83 | 84 | // Fonts 85 | // -------------------------------------------------- 86 | 87 | @import "roboto"; 88 | @import "noto-sans"; 89 | -------------------------------------------------------------------------------- /src/pages/botie-details/botie-details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Botie Details 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 |

28 | 29 | {{ token }} 30 |

31 | 32 | 33 | 34 |
35 | Congratulations! Wechaty is on duty. 36 |

Logined with user {{ user.name }}

37 |
38 | 39 |
40 | 45 | 46 | 47 |
48 | Wechaty Initializing... 49 |
50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | Heartbeat #{{ counter }} 58 | {{ timestamp }} 59 | 60 | 61 | 64 | 65 | 66 | {{ event.type }} 67 | 68 | at {{ event.time }} 69 | 70 | 71 |
72 | 73 |
    74 |
  1. 75 | {{ message }} 76 |
  2. 77 |
78 | 79 | 80 | 95 | 96 | 103 | 104 | 111 | 112 |
113 | -------------------------------------------------------------------------------- /src/pages/giftie-list/giftie-list.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import { Brolog } from 'brolog' 9 | 10 | import { UnlockPage } from '../unlock' 11 | 12 | interface Giftie { 13 | icon: string, 14 | title: string, 15 | description: string, 16 | } 17 | 18 | @IonicPage() 19 | @Component({ 20 | selector: 'page-giftie-list', 21 | templateUrl: 'giftie-list.html', 22 | }) 23 | export class GiftieListPage { 24 | public gifties: Giftie[] = [ 25 | { 26 | icon: 'undo', 27 | title: 'Auto Reply', 28 | description: `Send canned reply automatically for **keywords** set by you.`, 29 | }, 30 | { 31 | icon: 'people', 32 | title: 'Auto Accept Friend Request', 33 | description: `Accept friend request for you automatically. Can set **keywords** to only accept request with that.`, 34 | }, 35 | { 36 | icon: 'key', 37 | title: 'Room Inviter', 38 | description: `Send a room invitation to friend if received the specified **keywords**.`, 39 | }, 40 | { 41 | icon: 'eye', 42 | title: 'Room Guardian', 43 | description: `Protect your **room topic** form being changed by others.`, 44 | }, 45 | { 46 | icon: 'megaphone', 47 | title: 'Mass Message Sender', 48 | description: `Send messages to multiple users at once.`, 49 | }, 50 | { 51 | icon: 'swap', 52 | title: 'Interpreter', 53 | description: `Transpose messages from one language into another, instantly and accurately. 54 | Speaker can use Text or Audio, audience will get Text in the **Target Language**.`, 55 | }, 56 | { 57 | icon: 'film', 58 | title: 'Film Maker', 59 | description: `Concatenate multiple Videos/Audios that received.`, 60 | }, 61 | { 62 | icon: 'images', 63 | title: 'Image Digester', 64 | description: `Read photo and tell you what's it.`, 65 | }, 66 | { 67 | icon: 'eye', 68 | title: 'Message Tracker', 69 | description: `Notice you if any new message matches the keywords you set.`, 70 | }, 71 | { 72 | icon: 'microphone', 73 | title: 'Speech Recongnizer', 74 | description: `Write down the text message in a speech for you.`, 75 | }, 76 | ] 77 | 78 | constructor( 79 | public log: Brolog, 80 | public navCtrl: NavController, 81 | public navParams: NavParams, 82 | ) { 83 | this.log.verbose('GiftieListPage', 'constructor()') 84 | } 85 | 86 | public ionViewDidLoad() { 87 | this.log.verbose('GiftieListPage', 'ionViewDidLoad()') 88 | } 89 | 90 | public unlock() { 91 | this.navCtrl.push(UnlockPage) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/tslint-eslint-rules/dist/rules" 4 | ], 5 | "rules": { 6 | "align": [ 7 | true, 8 | "parameters", 9 | // "arguments", 10 | "statements" 11 | ], 12 | "jsdoc-require": [ 13 | false 14 | ], 15 | "ban": false, 16 | "class-name": true, 17 | "comment-format": [ 18 | true, 19 | "check-space" 20 | ], 21 | "curly": false, 22 | "eofline": true, 23 | "forin": false, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-name": false, 29 | "jsdoc-format": true, 30 | "label-position": true, 31 | "max-line-length": [ 32 | true, 33 | 180 34 | ], 35 | "callable-types": true, 36 | "import-blacklist": [true, "rxjs"], 37 | "interface-over-type-literal": true, 38 | "no-empty-interface": true, 39 | "no-string-throw": true, 40 | "prefer-const": true, 41 | "unified-signatures": false, 42 | "no-inferrable-types": [true, "ignore-params"], 43 | "member-access": true, 44 | "member-ordering": [false], 45 | "no-any": false, 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-conditional-assignment": true, 49 | "no-consecutive-blank-lines": true, 50 | "no-console": [false], 51 | "no-construct": false, 52 | "no-debugger": true, 53 | "no-duplicate-variable": true, 54 | "no-empty": true, 55 | "no-eval": true, 56 | "no-internal-module": true, 57 | "no-require-imports": false, 58 | "no-shadowed-variable": true, 59 | "no-string-literal": false, 60 | "no-switch-case-fall-through": true, 61 | "no-trailing-whitespace": true, 62 | "no-unused-expression": true, 63 | "no-unused-variable": [true], 64 | "no-use-before-declare": true, 65 | "no-var-keyword": true, 66 | "no-var-requires": false, 67 | "object-literal-sort-keys": false, 68 | "one-line": [ 69 | true, 70 | "check-open-brace", 71 | "check-whitespace" 72 | ], 73 | "quotemark": [ 74 | true, 75 | "single", 76 | "avoid-escape" 77 | ], 78 | "radix": false, 79 | "semicolon": [true, "never"], 80 | "switch-default": false, 81 | "trailing-comma": [ 82 | true, 83 | { 84 | "multiline": "always", 85 | "singleline": "never" 86 | } 87 | ], 88 | "triple-equals": [true], 89 | "typedef": [false], 90 | "typedef-whitespace": [ 91 | false, 92 | { 93 | "call-signature": "space", 94 | "index-signature": "nospace", 95 | "parameter": "nospace", 96 | "property-declaration": "nospace", 97 | "variable-declaration": "nospace" 98 | } 99 | ], 100 | "variable-name": [ 101 | true, 102 | "check-format", 103 | "allow-leading-underscore", 104 | "ban-keywords" 105 | ], 106 | "whitespace": [ 107 | true, 108 | "check-branch", 109 | "check-decl", 110 | "check-operator", 111 | "check-separator", 112 | "check-type" 113 | ] 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/pages/hostie-list/hostie-list.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & SPA 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | 10 | /** 11 | * An Introduction to Lists in Ionic 2 12 | * http://www.joshmorony.com/an-introduction-to-lists-in-ionic-2/ 13 | * 14 | */ 15 | import { 16 | ChangeDetectionStrategy, 17 | Component, 18 | OnInit, 19 | OnDestroy, 20 | } from '@angular/core' 21 | import { 22 | IonicPage, 23 | NavController, 24 | NavParams, 25 | reorderArray, 26 | } from 'ionic-angular' 27 | import { 28 | // Observable, 29 | Subscription, 30 | } from 'rxjs/Subscription' 31 | 32 | import { Brolog } from 'brolog' 33 | 34 | import { 35 | Hostie, 36 | HostieStore, 37 | Status, 38 | } from '@chatie/db' 39 | 40 | import { HostieCreatePage } from '../hostie-create/' 41 | import { HostieDetailsPage } from '../hostie-details/' 42 | 43 | @IonicPage() 44 | @Component({ 45 | selector: 'page-hostie-list', 46 | templateUrl: 'hostie-list.html', 47 | changeDetection: ChangeDetectionStrategy.OnPush, 48 | }) 49 | 50 | export class HostieListPage implements OnInit, OnDestroy { 51 | public hostieList: Hostie[] 52 | public hostieListSubscription: Subscription 53 | 54 | public reordering = false 55 | 56 | constructor( 57 | public log: Brolog, 58 | public hostieStore: HostieStore, 59 | public navCtrl: NavController, 60 | public navParams: NavParams, 61 | ) { 62 | this.log.verbose('HostieListPage', 'constructor()') 63 | } 64 | 65 | public ngOnInit() { 66 | this.log.verbose('HostieListPage', 'ngOnInit()') 67 | 68 | this.hostieListSubscription = this.hostieStore.itemList.subscribe(list => { 69 | this.log.silly('HostieListPage', 'ngOnInit() subscript list.length=%d', list.length) 70 | this.hostieList = list 71 | }) 72 | } 73 | 74 | public ngOnDestroy() { 75 | this.log.verbose('HostieListPage', 'ngOnDestroy()') 76 | 77 | if (this.hostieListSubscription) { 78 | this.hostieListSubscription.unsubscribe() 79 | } 80 | } 81 | 82 | public gotoHostieDetail(hostie: Hostie, event: any) { 83 | this.log.verbose('HostieListPage', 'select(%s, %s)', hostie.id, event) 84 | this.navCtrl.push(HostieDetailsPage, { 85 | hostie, 86 | }) 87 | } 88 | 89 | public toggleReordering() { 90 | this.reordering = !this.reordering 91 | } 92 | 93 | public reorder(indexes: number[]) { 94 | this.hostieList = reorderArray(this.hostieList, {from: 0, to: 1}) 95 | // TODO: save to backend 96 | } 97 | 98 | public hostieIcon(hostie: Hostie) { 99 | this.log.verbose('HostieListPage', 'hostieIcon()') 100 | 101 | if (hostie.status === Status.ON) { 102 | return 'ios-home' 103 | } 104 | return 'ios-home-outline' 105 | } 106 | 107 | public async trash(hostie: Hostie): Promise { 108 | this.log.verbose('HostieListPage', 'trash(%s)', hostie.id) 109 | if (!hostie.id) { 110 | throw new Error('no hostie id') 111 | } 112 | await this.hostieStore.delete(hostie.id) 113 | } 114 | 115 | public add() { 116 | this.navCtrl.push(HostieCreatePage) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/pages/login/login.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | OnDestroy, 5 | } from '@angular/core' 6 | import { 7 | AlertController, 8 | IonicPage, 9 | LoadingController, 10 | Loading, 11 | NavController, 12 | } from 'ionic-angular' 13 | 14 | import { 15 | Subscription, 16 | } from 'rxjs/Subscription' 17 | 18 | import { Auth } from 'auth-angular' 19 | import { Brolog } from 'brolog' 20 | 21 | import { VERSION } from '../../config' 22 | 23 | import { DashboardPage } from '../dashboard/' 24 | 25 | @IonicPage() 26 | @Component({ 27 | selector: 'page-login', 28 | templateUrl: 'login.html', 29 | }) 30 | export class LoginPage implements OnInit, OnDestroy { 31 | private validSub?: Subscription 32 | private loading?: Loading 33 | 34 | public email: string 35 | public password: string 36 | 37 | public version: string 38 | 39 | constructor( 40 | public alertCtrl: AlertController, 41 | public auth: Auth, 42 | public log: Brolog, 43 | public loadingCtrl: LoadingController, 44 | public navCtrl: NavController, 45 | ) { 46 | this.log.verbose('LoginPage', 'constructor()') 47 | 48 | this.version = VERSION 49 | } 50 | 51 | public ngOnInit() { 52 | this.log.verbose('LoginPage', 'ngOnInit()') 53 | 54 | this.validSub = this.auth.valid.subscribe(valid => { 55 | this.log.verbose('LoginPage', 'ngOnInit() this.auth.valid.subscribe(valid=%s)', valid) 56 | if (valid) { 57 | this.onLogin() 58 | } 59 | }) 60 | } 61 | 62 | // https://webcake.co/page-lifecycle-hooks-in-ionic-2/ 63 | public ngOnDestroy() { 64 | this.log.verbose('LoginPage', 'ngOnDestroy()') 65 | 66 | if (this.validSub) { 67 | this.validSub.unsubscribe() 68 | } 69 | } 70 | 71 | public ionViewDidLoad() { 72 | this.log.verbose('LoginPage', 'ionViewDidLoad()') 73 | } 74 | 75 | public onLogin(): void { 76 | this.log.verbose('LoginPage', 'onLogin()') 77 | this.gotoDashboardPage() 78 | } 79 | 80 | public async login(): Promise { 81 | this.log.verbose('LoginPage', 'login()') 82 | 83 | this.auth.login() 84 | 85 | // } catch (e) { 86 | // this.log.warn('LoginPage', 'login() exception: %s', e && e.message || e) 87 | 88 | // this.alertCtrl.create({ 89 | // title: 'Login Error', 90 | // subTitle: 'Exception: ' + e.message, 91 | // buttons: ['OK'], 92 | // }).present() 93 | // } 94 | 95 | } 96 | 97 | public async logout(): Promise { 98 | this.log.verbose('LoginPage', 'logout()') 99 | 100 | await this.auth.logout() 101 | this.navCtrl.setRoot(LoginPage) 102 | } 103 | 104 | public showLoader(): void { 105 | this.log.verbose('LoginPage', 'showLoader()') 106 | 107 | this.loading = this.loadingCtrl.create({ 108 | content: 'Loading...', 109 | }) 110 | this.loading.present() 111 | } 112 | 113 | public hideLoader(): void { 114 | this.log.verbose('LoginPage', 'hideLoader()') 115 | 116 | if (!this.loading) { 117 | return 118 | } 119 | this.loading.dismissAll() 120 | this.loading = undefined 121 | } 122 | 123 | public async gotoDashboardPage(): Promise { 124 | this.log.verbose('LoginPage', 'gotoDashboardPage()') 125 | try { 126 | await this.navCtrl.setRoot(DashboardPage) 127 | } catch (e) { 128 | this.log.verbose('LoginPage', 'gotoDashboardPage() exception:%s', e.message) 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/pages/hostie-details/hostie-details.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & SPA 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | import { 10 | ChangeDetectionStrategy, 11 | ChangeDetectorRef, 12 | Component, 13 | } from '@angular/core' 14 | 15 | import { 16 | AlertController, 17 | IonicPage, 18 | NavController, 19 | NavParams, 20 | } from 'ionic-angular' 21 | 22 | import { Brolog } from 'brolog' 23 | 24 | import { 25 | Hostie, 26 | HostieStore, 27 | Status, 28 | Platform, 29 | } from '@chatie/db' 30 | 31 | import { HostieEditPage } from '../hostie-edit/' 32 | 33 | @IonicPage() 34 | @Component({ 35 | selector: 'page-hostie-details', 36 | templateUrl: 'hostie-details.html', 37 | /** 38 | * http://stackoverflow.com/questions/34375624/angular-2-one-time-binding 39 | * https://angular.io/docs/ts/latest/api/core/index/ChangeDetectionStrategy-enum.html#!#OnPush-anchor 40 | * http://www.syntaxsuccess.com/viewarticle/change-detection-in-angular-2.0 41 | */ 42 | changeDetection: ChangeDetectionStrategy.OnPush, 43 | }) 44 | export class HostieDetailsPage { 45 | public hostie: Hostie 46 | public hostieStore: HostieStore 47 | 48 | constructor( 49 | public alertCtrl: AlertController, 50 | public log: Brolog, 51 | public cdRef: ChangeDetectorRef, 52 | public navCtrl: NavController, 53 | public navParams: NavParams, 54 | ) { 55 | this.log.verbose('HostieDetailsPage', 'constructor()') 56 | 57 | // If we navigated to this page, we will have an item available as a nav param 58 | this.hostie = navParams.get('hostie') 59 | this.log.silly('HostieDetailsPage', 'constructor() hostie id:%s', this.hostie.id) 60 | } 61 | 62 | public online(): boolean { 63 | this.log.verbose('HostieDetailsPage', 'online()') 64 | return this.hostie.status === Status.ON 65 | } 66 | 67 | public uptime(): number { 68 | this.log.verbose('HostieDetailsPage', 'uptime()') 69 | return Date.now() - 0 // FIXME 70 | } 71 | 72 | /** 73 | * http://ionicframework.com/docs/ionicons/ 74 | */ 75 | public icon(): string { 76 | switch (this.hostie.platform) { 77 | case Platform.UNKNOWN: return 'help' 78 | case Platform.DOCKER: return 'cube' 79 | case Platform.LINUX: return 'logo-tux' 80 | case Platform.WIN32: return 'logo-windows' 81 | case Platform.DARWIN: return 'logo-apple' 82 | default: return 'help' 83 | } 84 | } 85 | 86 | public copy() { 87 | this.alertCtrl.create({ 88 | title: 'Copy TOKEN', 89 | subTitle: 'Use this string as WECHATY_TOKEN', 90 | inputs: [ 91 | { 92 | label: 'Token', 93 | name: 'TOKEN', 94 | placeholder: 'Token', 95 | value: this.hostie.token, 96 | disabled: true, 97 | }, 98 | ], 99 | buttons: ['Done'], 100 | }).present() 101 | } 102 | 103 | public edit() { 104 | this.log.verbose('HostieDetailsPage', 'edit() hostie #%s', this.hostie.id) 105 | 106 | this.navCtrl.push(HostieEditPage, { 107 | hostie: this.hostie, 108 | /** 109 | * [SOLVED] Ionic2 navController pop with params 110 | * https://forum.ionicframework.com/t/solved-ionic2-navcontroller-pop-with-params/58104 111 | */ 112 | notify: (savedHostie: Hostie) => { 113 | this.log.verbose('HostieDetailsPage', 'edit() done() %s', 114 | JSON.stringify(savedHostie), 115 | ) 116 | this.hostie = savedHostie 117 | this.cdRef.markForCheck() 118 | }, 119 | }) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@chatie/app", 3 | "description": "Chatie App", 4 | "version": "0.2.49", 5 | "author": "Huan LI ", 6 | "homepage": "https://github.com/chatie/app/", 7 | "license": "Apache-2.0", 8 | "bugs": { 9 | "url": "https://github.com/chatie/app/issues" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/chatie/app.git" 14 | }, 15 | "private": true, 16 | "scripts": { 17 | "build": "ionic-app-scripts build", 18 | "build:browser:prod": "ionic-app-scripts build --prod --aot", 19 | "clean": "ionic-app-scripts clean", 20 | "deploy:browser:surge": "bash scripts/deploy-surge.sh", 21 | "ionic:build": "ionic-app-scripts build", 22 | "ionic:build:browser": "ionic cordova build browser --aot --prod", 23 | "ionic:monitoring:syncmaps": "(sleep 1 && echo \"y\n\" && sleep 1 && echo \"y\n\") | ionic monitoring syncmaps", 24 | "ionic:serve": "ionic-app-scripts serve", 25 | "lite:server": "lite-server", 26 | "lint": "npm run lint:ts && npm run lint:ionic", 27 | "lint:ts": "tslint --project tsconfig.json && tsc --noEmit", 28 | "lint:ionic": "ionic-app-scripts lint", 29 | "test": "npm run lint && echo To Be Test", 30 | "test:pack": "echo To Be Pack" 31 | }, 32 | "dependencies": { 33 | "@angular/animations": "6.1.1", 34 | "@angular/common": "6.1.1", 35 | "@angular/compiler": "6.1.1", 36 | "@angular/compiler-cli": "6.1.1", 37 | "@angular/core": "6.1.1", 38 | "@angular/forms": "6.1.1", 39 | "@angular/http": "6.1.1", 40 | "@angular/platform-browser": "6.1.1", 41 | "@angular/platform-browser-dynamic": "6.1.1", 42 | "@auth0/angular-jwt": "^2.0.0", 43 | "@chatie/angular": "^0.4.8", 44 | "@chatie/db": "^0.8.21", 45 | "@chatie/graphql": "^0.6.7", 46 | "@ionic-native/core": "4.11.0", 47 | "@ionic-native/splash-screen": "4.11.0", 48 | "@ionic-native/status-bar": "4.11.0", 49 | "@ionic/pro": "^2.0.3", 50 | "@ionic/storage": "2.1.3", 51 | "angularx-qrcode": "^1.1.7", 52 | "auth-angular": "^0.4.5", 53 | "auth0-lock": "^11.5.1", 54 | "brolog": "^1.3.3", 55 | "cordova-android": "7.1.1", 56 | "cordova-browser": "5.0.3", 57 | "cordova-ios": "4.5.5", 58 | "cordova-plugin-device": "^2.0.1", 59 | "cordova-plugin-ionic": "^5.0.5", 60 | "cordova-plugin-ionic-keyboard": "^2.0.5", 61 | "cordova-plugin-ionic-webview": "^2.0.2", 62 | "cordova-plugin-splashscreen": "^5.0.2", 63 | "cordova-plugin-whitelist": "^1.3.3", 64 | "cuid": "^2.1.1", 65 | "graphql-tag": "^2.8.0", 66 | "ionic-angular": "3.9.2", 67 | "ionicons": "4.3.0", 68 | "jwt-decode": "^2.2.0", 69 | "moment": "^2.21.0", 70 | "rxjs": "6.2.2", 71 | "state-switch": "^0.6.2", 72 | "sw-toolbox": "3.6.0", 73 | "zone.js": "0.8.26" 74 | }, 75 | "devDependencies": { 76 | "@angular/cli": "^6.1.2", 77 | "@ionic/app-scripts": "^3.1.8", 78 | "@types/auth0-lock": "^11.4.5", 79 | "@types/cuid": "^1.3.0", 80 | "@types/node": "^10.5.6", 81 | "babel-eslint": "^8.2.2", 82 | "cordova": "^8.0.0", 83 | "eslint": "^5.3.0", 84 | "git-scripts": "^0.2.1", 85 | "ionic": "^4.0.3", 86 | "lite-server": "^2.3.0", 87 | "surge": "^0.20.1", 88 | "typescript": "~3.0.1" 89 | }, 90 | "cordova": { 91 | "plugins": { 92 | "cordova-plugin-whitelist": {}, 93 | "cordova-plugin-device": {}, 94 | "cordova-plugin-splashscreen": {}, 95 | "cordova-plugin-ionic-webview": {}, 96 | "cordova-plugin-ionic": { 97 | "APP_ID": "053e819b", 98 | "CHANNEL_NAME": "Master", 99 | "UPDATE_METHOD": "background", 100 | "WARN_DEBUG": "true", 101 | "UPDATE_API": "https://api.ionicjs.com", 102 | "MAX_STORE": "2" 103 | } 104 | }, 105 | "platforms": [ 106 | "ios", 107 | "browser" 108 | ] 109 | }, 110 | "git": { 111 | "scripts": { 112 | "pre-push": "./scripts/pre-push.sh" 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/pages/dashboard/dashboard.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & SPA 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | import { 10 | Component, 11 | OnInit, 12 | OnDestroy, 13 | } from '@angular/core' 14 | import { 15 | IonicPage, 16 | NavController, 17 | NavParams, 18 | } from 'ionic-angular' 19 | import { 20 | Subscription, 21 | } from 'rxjs/Rx' 22 | 23 | import { Auth } from 'auth-angular' 24 | import { Brolog } from 'brolog' 25 | 26 | import { 27 | Botie, 28 | BotieStore, 29 | // Giftie, 30 | GiftieStore, 31 | Status, 32 | Hostie, 33 | HostieStore, 34 | } from '@chatie/db' 35 | 36 | import { BotieListPage } from '../botie-list/' 37 | import { HostieListPage } from '../hostie-list/' 38 | 39 | @IonicPage() 40 | @Component({ 41 | selector: 'page-dashboard', 42 | templateUrl: 'dashboard.html', 43 | }) 44 | export class DashboardPage implements OnInit, OnDestroy { 45 | private hostieSubscription?: Subscription 46 | private botieSubscription?: Subscription 47 | 48 | public hostieList: Hostie[] 49 | public hostieOnlineNum: number 50 | 51 | public botieList: Botie[] 52 | public botieOnlineNum: number 53 | 54 | constructor( 55 | public auth: Auth, 56 | public botieStore: BotieStore, 57 | public hostieStore: HostieStore, 58 | public giftieStore: GiftieStore, 59 | public log: Brolog, 60 | public navCtrl: NavController, 61 | public navParams: NavParams, 62 | ) { 63 | this.log.verbose('DashboardPage', 'constructor()') 64 | 65 | this.hostieList = [] 66 | this.hostieOnlineNum = 0 67 | 68 | this.botieList = [] 69 | this.botieOnlineNum = 0 70 | } 71 | 72 | public ionViewDidLoad() { 73 | this.log.verbose('DashboardPage', 'ionViewDidLoad()') 74 | } 75 | 76 | // https://devdactic.com/ionic-auth-guards/ 77 | public ionViewCanEnter() { 78 | this.log.verbose('DashboardPage', 'ionViewCanEnter()') 79 | return this.auth.valid.first().toPromise() 80 | } 81 | 82 | // https://webcake.co/page-lifecycle-hooks-in-ionic-2/ 83 | public ngOnInit() { 84 | this.log.verbose('DashboardPage', 'ngOnInit()') 85 | // console.log(this.hostieStore) 86 | // console.log(this.hostieStore.itemList) 87 | // console.log(this.hostieStore.itemList.subscribe) 88 | 89 | this.botieSubscription = this.botieStore.itemList.subscribe(list => { 90 | this.log.verbose('DashboardPage', 'ngOnInit() botieStore.itemList.subscribe() list.length=%d', list.length) 91 | this.botieList = list 92 | this.botieOnlineNum = list 93 | .filter(item => item.status === Status.ON ) 94 | .length 95 | }) 96 | 97 | this.hostieSubscription = this.hostieStore.itemList.subscribe(list => { 98 | this.log.verbose('DashboardPage', 'ngOnInit() hostieStore.itemList.subscribe() list.length=%d', list.length) 99 | this.hostieList = list 100 | this.hostieOnlineNum = list 101 | .filter( item => item.status === Status.ON ) 102 | .length 103 | }) 104 | 105 | } 106 | 107 | public ngOnDestroy() { 108 | this.log.verbose('DashboardPage', 'ngOnDestroy()') 109 | 110 | if (this.botieSubscription) { 111 | this.botieSubscription.unsubscribe() 112 | this.botieSubscription = undefined 113 | } 114 | 115 | if (this.hostieSubscription) { 116 | this.hostieSubscription.unsubscribe() 117 | this.hostieSubscription = undefined 118 | } 119 | 120 | } 121 | 122 | public gotoHostieListPage() { 123 | this.log.verbose('DashboardPage', 'gotoHostieListPage()') 124 | this.navCtrl.push(HostieListPage) 125 | } 126 | 127 | public gotoBotieListPage() { 128 | this.log.verbose('DashboardPage', 'gotoBotieListPage()') 129 | this.navCtrl.push(BotieListPage) 130 | } 131 | 132 | // gotoBotieDetailsPage() { 133 | // this.log.verbose('DashboardPage', 'gotoBotieDetailsPage()') 134 | // this.navCtrl.push(BotieDetailsPage) 135 | // } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/pages/botie-details/botie-details.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | 8 | import moment from 'moment' 9 | 10 | import { Brolog } from 'brolog' 11 | 12 | import { 13 | WechatyComponent, 14 | ScanInfo, 15 | UserInfo, 16 | } from '@chatie/angular' 17 | 18 | import { 19 | // Botie, 20 | } from '@chatie/db' 21 | 22 | interface WechatyEvent { 23 | type: 'scan' | 'login' | 'logout' | 'message' | 'error', 24 | time: string, 25 | data: string, 26 | } 27 | 28 | @IonicPage() 29 | @Component({ 30 | selector: 'page-botie-details', 31 | templateUrl: 'botie-details.html', 32 | }) 33 | export class BotieDetailsPage { 34 | 35 | public token: string 36 | public messageList: string[] 37 | 38 | public scanQrcodeValue: null | string 39 | 40 | public user: UserInfo | null 41 | public counter: number 42 | 43 | public timestamp: string 44 | 45 | public eventList: WechatyEvent[] 46 | 47 | // private botie: Botie 48 | 49 | constructor( 50 | public log: Brolog, 51 | public navCtrl: NavController, 52 | public navParams: NavParams, 53 | ) { 54 | this.token = navParams.get('token') 55 | 56 | this.log.verbose('BotieDetails', 'constructor() with token:%s', this.token) 57 | } 58 | 59 | public ionViewDidLoad() { 60 | this.log.verbose('BotieDetails', 'ionViewDidLoad()') 61 | } 62 | 63 | public ngOnInit() { 64 | this.log.verbose('BotieDetailsPage', 'ngOnInit()') 65 | 66 | this.eventList = [] 67 | this.messageList = [] 68 | this.counter = 0 69 | } 70 | 71 | public ngOnDestroy() { 72 | this.log.verbose('BotieDetailsPage', 'ngOnDestroy()') 73 | } 74 | 75 | public onMessage(msg: any) { 76 | this.log.verbose('BotieDetailsPage', 'onMessage(%s)', msg) 77 | this.messageList.push(msg) 78 | this.eventList.push({ 79 | type: 'message', 80 | time: moment().format('LTS'), 81 | data: msg, 82 | }) 83 | } 84 | 85 | public onHeartbeat(e: any) { 86 | this.log.silly('BotieDetailsPage', 'onHeartbeat(%s)', e) 87 | this.counter++ 88 | this.timestamp = moment().format('LTS') 89 | } 90 | 91 | public onScan(scan: ScanInfo) { 92 | this.log.verbose('BotieDetailsPage', 'onScan(%s)', JSON.stringify(scan)) 93 | 94 | this.scanQrcodeValue = scan.qrcode 95 | 96 | // console.log(scan) 97 | 98 | this.eventList.push({ 99 | type: 'scan', 100 | time: moment().format('LTS'), 101 | data: scan.qrcode, 102 | }) 103 | } 104 | 105 | public onLogin(user: UserInfo) { 106 | this.log.verbose('BotieDetailsPage', 'onLogin(%s)', user.name) 107 | this.user = user 108 | this.scanQrcodeValue = null 109 | 110 | console.log(user) 111 | 112 | this.eventList.push({ 113 | type: 'login', 114 | time: moment().format('LTS'), 115 | data: user.name, 116 | }) 117 | } 118 | 119 | public onLogout(e: UserInfo) { 120 | this.log.verbose('BotieDetailsPage', 'onLogout(%s)', e.name) 121 | this.user = null 122 | this.eventList.push({ 123 | type: 'logout', 124 | time: moment().format('LTS'), 125 | data: e.name, 126 | }) 127 | } 128 | 129 | public onError(e: any) { 130 | this.log.verbose('BotieDetailsPage', 'onError(%s)', e) 131 | this.eventList.push({ 132 | type: 'error', 133 | time: moment().format('LTS'), 134 | data: e, 135 | }) 136 | } 137 | 138 | public shutdown(wechaty: WechatyComponent) { 139 | this.log.verbose('BotieDetailsPage', 'shutdown()') 140 | this.scanQrcodeValue = this.user = null 141 | wechaty.shutdown('by web bot component') 142 | } 143 | 144 | public logout(wechaty: WechatyComponent) { 145 | this.log.verbose('BotieDetailsPage', 'logout()') 146 | this.scanQrcodeValue = this.user = null 147 | wechaty.logoff('from chatie app') 148 | } 149 | 150 | public eventToIcon(eventName: string): string { 151 | switch (eventName) { 152 | case 'scan': return 'qr-scanner' 153 | case 'login': return 'log-in' 154 | case 'logout': return 'log-out' 155 | case 'message': return 'chatboxes' 156 | case 'error': return 'alert' 157 | default: return 'help' 158 | } 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/pages/about/about.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | AlertController, 4 | Loading, 5 | LoadingController, 6 | IonicPage, 7 | NavController, 8 | NavParams, 9 | ToastController, 10 | } from 'ionic-angular' 11 | 12 | import { Brolog } from 'brolog' 13 | 14 | import { HelpPage } from '../help/' 15 | import { StatusPage } from '../status/' 16 | 17 | @IonicPage() 18 | @Component({ 19 | selector: 'page-about', 20 | templateUrl: 'about.html', 21 | }) 22 | export class AboutPage { 23 | private loading: Loading | null = null 24 | public version = '0.0.0' 25 | public clickCounter = 0 26 | 27 | constructor( 28 | public alertCtrl: AlertController, 29 | public log: Brolog, 30 | public loadingCtrl: LoadingController, 31 | public navCtrl: NavController, 32 | public navParams: NavParams, 33 | public toastCtrl: ToastController, 34 | ) { 35 | this.log.verbose('AboutPage', 'constructor()') 36 | this.initVersion() 37 | } 38 | 39 | public initVersion() { 40 | this.log.verbose('AboutPage', 'getVersion()') 41 | 42 | try { 43 | // FIXME 44 | const packageJson = require('../../../package.json') 45 | this.version = packageJson.version 46 | } catch (e) { 47 | this.log.warn('AboutPage', 'getVersion() exception: %s', e.message) 48 | } 49 | } 50 | 51 | public ionViewDidLoad() { 52 | this.log.verbose('AboutPage', 'ionViewDidLoad()') 53 | } 54 | 55 | public help() { 56 | this.navCtrl.push(HelpPage) 57 | } 58 | 59 | public status() { 60 | this.navCtrl.push(StatusPage) 61 | } 62 | 63 | // async checkDeploy() { 64 | // this.log.verbose('AboutPage', 'check() clickCounter=%s', this.clickCounter) 65 | 66 | // if (this.loading) { 67 | // return 68 | // } 69 | 70 | // const MAX_NUM = 7 71 | // if (this.clickCounter <= MAX_NUM) { 72 | // this.toastCtrl.create({ 73 | // message: (MAX_NUM - this.clickCounter).toString(), 74 | // duration: 500, 75 | // position: 'middle', 76 | // }).present() 77 | 78 | // this.clickCounter++ 79 | // return 80 | // } 81 | 82 | // this.deploy.channel = 'dev' 83 | // this.showLoader() 84 | 85 | // try { 86 | // const hasUpdate = await this.deploy.check() 87 | 88 | // if (!hasUpdate) { 89 | // this.hideLoader() 90 | 91 | // this.toastCtrl.create({ 92 | // message: 'You are cool!', 93 | // duration: 1500, 94 | // position: 'middle', 95 | // }) 96 | // .present() 97 | 98 | // return 99 | 100 | // } 101 | 102 | // this.log.silly('AboutPage', 'checkDeploy() found new update!') 103 | 104 | // const metaData = this.deploy.getMetadata() 105 | // this.log.silly('AboutPage', 'check() metaData of update: %s', JSON.stringify(metaData)) 106 | 107 | // this.log.silly('AboutPage', 'check() downloading...') 108 | // await this.deploy.download() 109 | 110 | // this.log.silly('AboutPage', 'check() extracting...') 111 | // await this.deploy.extract() 112 | 113 | // const snapshotList = await this.deploy.getSnapshots() as string[] 114 | // this.log.silly('AboutPage', 'check() we has %s snapshots: %s', 115 | // snapshotList.length, 116 | // snapshotList.join(','), 117 | // ) 118 | 119 | // this.hideLoader() 120 | 121 | // this.log.silly('AboutPage', 'check() loading...') 122 | // this.deploy.load() 123 | 124 | // } catch (e) { 125 | // this.log.warn('AboutPage', 'check() exception: %s', e.message) 126 | 127 | // this.hideLoader() 128 | // this.alertCtrl.create({ 129 | // title: 'Check Error', 130 | // subTitle: 'Exception: ' + e.message, 131 | // buttons: ['OK'], 132 | // }).present() 133 | // } 134 | // } 135 | 136 | public showLoader(): void { 137 | this.log.verbose('AboutPage', 'showLoader()') 138 | 139 | this.loading = this.loadingCtrl.create({ 140 | content: 'Loading...', 141 | }) 142 | this.loading.present() 143 | } 144 | 145 | public hideLoader(): void { 146 | this.log.verbose('AboutPage', 'hideLoader()') 147 | 148 | if (!this.loading) { 149 | return 150 | } 151 | this.loading.dismissAll() 152 | this.loading = null 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/pages/setting/setting.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { 3 | IonicPage, 4 | NavController, 5 | NavParams, 6 | } from 'ionic-angular' 7 | import { 8 | Pro, 9 | } from '@ionic/pro' 10 | 11 | import { Brolog } from 'brolog' 12 | 13 | import { AboutPage } from '../about/' 14 | import { HelpPage } from '../help/' 15 | import { LogoutPage } from '../logout/' 16 | 17 | @IonicPage() 18 | @Component({ 19 | selector: 'page-setting', 20 | templateUrl: 'setting.html', 21 | }) 22 | export class SettingPage { 23 | public notificate = true 24 | 25 | public deployChannel = '' 26 | public isBeta = false 27 | public downloadProgress = 0 28 | 29 | constructor( 30 | public log: Brolog, 31 | public navCtrl: NavController, 32 | public navParams: NavParams, 33 | ) { 34 | this.log.verbose('SettingPage', 'constructor()') 35 | this.checkChannel() 36 | } 37 | 38 | public ionViewDidLoad() { 39 | this.log.verbose('SettingPage', 'ionViewDidLoad()') 40 | } 41 | 42 | public about() { 43 | this.navCtrl.push(AboutPage) 44 | } 45 | 46 | public help() { 47 | this.navCtrl.push(HelpPage) 48 | } 49 | 50 | public gotoLogoutPage() { 51 | this.navCtrl.push(LogoutPage) 52 | } 53 | 54 | public testMonitoring() { 55 | 56 | Pro.monitoring.exception(new Error('test Pro.monitoring.exception()')) 57 | Pro.monitoring.log('test Pro.monitoring.log() This happens sometimes for level: error', { level: 'error' }) 58 | 59 | try { 60 | Pro.monitoring.call(() => { 61 | throw new Error('test Pro.monitoring.call() error') 62 | }) 63 | } catch (e) { 64 | console.log('call function exception still be throwed to outside') 65 | } 66 | 67 | const newFn = Pro.monitoring.wrap(() => { 68 | throw new Error('test Pro.monitoring.wrap newFn() error') 69 | }) 70 | try { 71 | newFn() 72 | } catch (e) { 73 | console.log('call wrap func error still be throwed to outside') 74 | } 75 | 76 | } 77 | 78 | /** 79 | * https://ionicframework.com/docs/pro/deploy/plugin-api.html 80 | */ 81 | public async checkChannel() { 82 | try { 83 | const res = await Pro.deploy.info() 84 | this.deployChannel = res.channel || 'unknown(web?)' 85 | this.isBeta = (this.deployChannel === 'Beta') 86 | } catch (err) { 87 | // We encountered an error. 88 | // Here's how we would log it to Ionic Pro Monitoring while also catching: 89 | 90 | Pro.monitoring.exception(err) 91 | } 92 | } 93 | 94 | public async toggleBeta() { 95 | const config = { 96 | channel: (this.isBeta ? 'Beta' : 'Production'), 97 | } 98 | 99 | try { 100 | await Pro.deploy.init(config) 101 | await this.checkChannel() 102 | await this.performAutomaticUpdate() // Alternatively, to customize how this works, use performManualUpdate() 103 | } catch (err) { 104 | // We encountered an error. 105 | // Here's how we would log it to Ionic Pro Monitoring while also catching: 106 | 107 | Pro.monitoring.exception(err) 108 | } 109 | 110 | } 111 | 112 | public async performAutomaticUpdate() { 113 | 114 | /* 115 | This code performs an entire Check, Download, Extract, Redirect flow for 116 | you so you don't have to program the entire flow yourself. This should 117 | work for a majority of use cases. 118 | */ 119 | 120 | try { 121 | const resp = await Pro.deploy.checkAndApply(true, progress => { 122 | this.downloadProgress = progress 123 | }) 124 | 125 | if (resp.update) { 126 | // We found an update, and are in process of redirecting you since you put true! 127 | } else { 128 | // No update available 129 | } 130 | } catch (err) { 131 | // We encountered an error. 132 | // Here's how we would log it to Ionic Pro Monitoring while also catching: 133 | 134 | Pro.monitoring.exception(err) 135 | } 136 | } 137 | 138 | public async performManualUpdate() { 139 | 140 | /* 141 | Here we are going through each manual step of the update process: 142 | Check, Download, Extract, and Redirect. 143 | This code is currently exactly the same as performAutomaticUpdate, 144 | but you could split it out to customize the flow. 145 | 146 | Ex: Check, Download, Extract when a user logs into your app, 147 | but Redirect when they logout for an app that is always running 148 | but used with multiple users (like at a doctors office). 149 | */ 150 | 151 | try { 152 | const haveUpdate = await Pro.deploy.check() 153 | 154 | if (haveUpdate) { 155 | this.downloadProgress = 0 156 | 157 | await Pro.deploy.download(progress => { 158 | this.downloadProgress = progress 159 | }) 160 | await Pro.deploy.extract() 161 | await Pro.deploy.redirect() 162 | } 163 | } catch (err) { 164 | // We encountered an error. 165 | // Here's how we would log it to Ionic Pro Monitoring while also catching: 166 | 167 | Pro.monitoring.exception(err) 168 | } 169 | 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & SPA 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | import { 10 | Component, 11 | ViewChild, 12 | } from '@angular/core' 13 | import { 14 | Platform, 15 | MenuController, 16 | Nav, 17 | } from 'ionic-angular' 18 | import { 19 | StatusBar, 20 | } from '@ionic-native/status-bar' 21 | import { 22 | SplashScreen, 23 | } from '@ionic-native/splash-screen' 24 | import { Auth } from 'auth-angular' 25 | import { Brolog } from 'brolog' 26 | 27 | // import { BotieListPage } from '../pages/botie-list/' 28 | import { DashboardPage } from '../pages/dashboard/' 29 | // import { HostieListPage } from '../pages/hostie-list/' 30 | // import { FeedbackPage } from '../pages/feedback/' 31 | import { LoginPage } from '../pages/login/' 32 | // import { SettingPage } from '../pages/setting/' 33 | // import { WelcomePage } from '../pages/welcome/' 34 | 35 | // Week Type Detection of TypeScript 2.4 36 | // https://blog.mariusschulz.com/2017/12/01/typescript-2-4-weak-type-detection 37 | 38 | @Component({ 39 | templateUrl: 'app.html', 40 | }) 41 | export class ChatieApp { 42 | @ViewChild(Nav) private nav: Nav 43 | 44 | public rootPage: any = DashboardPage 45 | 46 | public pages: Array<{ 47 | title: string, 48 | icon: string, 49 | component: any, 50 | }> 51 | 52 | constructor( 53 | public auth: Auth, 54 | public log: Brolog, 55 | public menu: MenuController, 56 | public platform: Platform, 57 | public statusBar: StatusBar, 58 | public splashScreen: SplashScreen, 59 | ) { 60 | this.log.verbose('ChatieApp', 'constructor()') 61 | 62 | this.initializeApp() 63 | 64 | // set our app's pages 65 | this.pages = [ 66 | { title: 'Dashboard' , icon: 'speedometer' , component: DashboardPage }, 67 | // { title: 'Gifties' , icon: 'school' , component: GiftieListPage }, 68 | // { title: 'Gifties' , icon: 'flash' , component: GiftieListPage }, 69 | { title: 'Boties' , icon: 'logo-android', component: 'BotieListPage' }, 70 | { title: 'Hosties' , icon: 'home' , component: 'HostieListPage' }, 71 | { title: 'Setting' , icon: 'cog' , component: 'SettingPage' }, 72 | { title: 'Feedback' , icon: 'people' , component: 'FeedbackPage' }, 73 | ] 74 | } 75 | 76 | public async initializeApp() { 77 | this.log.verbose('ChatieApp', 'initializeApp()') 78 | 79 | const readySource = await this.platform.ready() 80 | 81 | this.log.silly('ChatieApp', 'initializeApp() platform.ready() return %s', readySource) 82 | 83 | // Okay, so the platform is ready and our plugins are available. 84 | // Here you can do any higher level native things you might need. 85 | 86 | // this.auth.idToken.subscribe(token => { 87 | // this.setupPush(!!token) 88 | // }) 89 | 90 | this.statusBar.styleDefault() 91 | this.splashScreen.hide() 92 | /** 93 | * https://www.raymondcamden.com/2016/11/04/an-example-of-the-ionic-auth-service-with-ionic-2 94 | */ 95 | this.auth.valid.subscribe(valid => { 96 | if (valid) { 97 | this.rootPage = DashboardPage 98 | } else { 99 | this.rootPage = LoginPage 100 | } 101 | }) 102 | 103 | return readySource 104 | } 105 | 106 | public openPage(page: any) { 107 | this.log.verbose('ChatieApp', 'openPage(%s)', page.title || page) 108 | 109 | // close the menu when clicking a link from the menu 110 | this.menu.close() 111 | 112 | // Reset the content nav to have just this page 113 | // we wouldn't want the back button to show in this scenario 114 | this.nav.setRoot(page.component) 115 | } 116 | 117 | // /** 118 | // * Setup Push Service 119 | // */ 120 | // async setupPush(push = true): Promise { 121 | // this.log.verbose('ChatieApp', 'setupPush(%s)', push) 122 | 123 | // try { 124 | // if (push) { 125 | // const pushToken = await this.push.register() 126 | // await this.push.saveToken(pushToken) 127 | // this.log.silly('ChatieApp', 'setupPush() push token saved: %s', pushToken) 128 | 129 | // this.pushSubscription = this.push.rx.notification().subscribe(msg => { 130 | // this.onPush(msg) 131 | // }) 132 | // } else { 133 | // if (this.pushSubscription) { 134 | // this.pushSubscription.unsubscribe() 135 | // this.pushSubscription = null 136 | // } 137 | // await this.push.unregister() 138 | // } 139 | // } catch (e) { 140 | // this.log.warn('AppComponent', 'setupPush() exception:%s', e.message) 141 | // } 142 | 143 | // // do something with the push data 144 | // // then call finish to let the OS know we are done 145 | // // push.finish(function() { 146 | // // console.log("processing of push data is finished"); 147 | // // }, function() { 148 | // // console.log("something went wrong with push.finish for ID = " + data.additionalData.notId) 149 | // // }, data.additionalData.notId); 150 | 151 | // } 152 | 153 | // onPush(msg: IPushMessage): void { 154 | // this.log.verbose('ChatieApp', 'onPush({title:%s,...})', msg.title) 155 | // alert(msg.title + ': ' + msg.text) 156 | // } 157 | } 158 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Chatie 4 | Your Cloud Bot Manager 5 | Chatie Team 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Chatie APP for Android & Ios & Web 3 | * Your ChatBot Pocket Manager 4 | * 5 | * https://github.com/chatie/app 6 | * Huan LI 7 | * License Apache-2.0 8 | */ 9 | import { BrowserModule } from '@angular/platform-browser' 10 | import { 11 | APP_INITIALIZER, 12 | ErrorHandler, 13 | Injectable, 14 | Injector, 15 | NgModule, 16 | } from '@angular/core' 17 | import { 18 | IonicStorageModule, 19 | // Storage, 20 | } from '@ionic/storage' 21 | 22 | import { 23 | IonicApp, 24 | IonicModule, 25 | IonicErrorHandler, 26 | } from 'ionic-angular' 27 | 28 | // These are all imports required for Pro Client with Monitoring & Deploy, 29 | // feel free to merge into existing imports above. 30 | import { Pro } from '@ionic/pro' 31 | import { StatusBar } from '@ionic-native/status-bar' 32 | import { SplashScreen } from '@ionic-native/splash-screen' 33 | 34 | import { HttpClientModule } from '@angular/common/http' 35 | 36 | import { 37 | Brolog, 38 | log, 39 | } from 'brolog' 40 | 41 | // import { WechatyModule } from '@chatie/angular' 42 | import { 43 | DbModule, 44 | } from '@chatie/db' 45 | 46 | import { AuthModule } from 'auth-angular' 47 | 48 | import { 49 | VERSION, 50 | } from '../config' 51 | 52 | import { ChatieApp } from './app.component' 53 | 54 | const { app_id } = require('../../ionic.config.json') 55 | Pro.init(app_id, { 56 | appVersion: VERSION, 57 | }) 58 | 59 | log.info('ChatieApp', 'v%s', VERSION) 60 | 61 | @Injectable() 62 | export class ProErrorHandler implements ErrorHandler { 63 | private ionicErrorHandler: IonicErrorHandler 64 | 65 | constructor(injector: Injector) { 66 | try { 67 | this.ionicErrorHandler = injector.get(IonicErrorHandler) 68 | } catch (e) { 69 | // Unable to get the IonicErrorHandler provider, ensure 70 | // IonicErrorHandler has been added to the providers list below 71 | throw e 72 | } 73 | } 74 | 75 | public handleError(err: any): void { 76 | Pro.monitoring.handleNewError(err) 77 | // Remove this if you want to disable Ionic's auto exception handling 78 | // in development mode. 79 | this.ionicErrorHandler.handleError(err) 80 | } 81 | } 82 | 83 | // const cloudSettings: CloudSettings = { 84 | // core: { 85 | // app_id, // : ionicConfig['app_id'], 86 | // }, 87 | // push: { 88 | // sender_id: '673602949542', 89 | // pluginConfig: { 90 | // ios: { 91 | // badge: true, 92 | // sound: true, 93 | // }, 94 | // android: { 95 | // iconColor: '#343434', 96 | // }, 97 | // }, 98 | // }, 99 | // // 'database': { 100 | // // 'authType': 'authenticated', 101 | // // }, 102 | // } 103 | 104 | /** 105 | * << Pages >> 106 | */ 107 | import { AboutPageModule } from '../pages/about/' 108 | import { BotieCreatePageModule } from '../pages/botie-create/' 109 | import { BotieListPageModule } from '../pages/botie-list/' 110 | import { BotieDetailsPageModule } from '../pages/botie-details/' 111 | import { DashboardPageModule } from '../pages/dashboard/' 112 | import { FeedbackPageModule } from '../pages/feedback/' 113 | import { GiftieListPageModule } from '../pages/giftie-list/' 114 | import { HelpPageModule } from '../pages/help/' 115 | import { HostieDetailsPageModule } from '../pages/hostie-details/' 116 | import { HostieEditPageModule } from '../pages/hostie-edit/' 117 | import { HostieListPageModule } from '../pages/hostie-list/' 118 | import { HostieCreatePageModule } from '../pages/hostie-create/' 119 | import { LoginPageModule } from '../pages/login/' 120 | import { LogoutPageModule } from '../pages/logout/' 121 | import { SettingPageModule } from '../pages/setting/' 122 | import { StatusPageModule } from '../pages/status/' 123 | import { UnlockPageModule } from '../pages/unlock/' 124 | import { WelcomePageModule } from '../pages/welcome/' 125 | 126 | export function appInitializerFactory() { 127 | log.verbose('AppModule', 'appInitializerFactory()') 128 | // Be careful: this will block the ui when starting the app 129 | return appInit 130 | 131 | async function appInit () { 132 | log.verbose('AppModule', 'appInitializerFactory() appInit()') 133 | // FIXME: Should not wait db open() 134 | // design better observable to chain the events. 135 | // await db.open() 136 | } 137 | } 138 | 139 | @NgModule({ 140 | declarations: [ 141 | ChatieApp, 142 | // << Pages >> 143 | // BotieListPage, 144 | // BotieDetailsPage, 145 | // DashboardPage, 146 | // FeedbackPage, 147 | // GiftieListPage, 148 | // HelpPage, 149 | // HostieCreatePage, 150 | // HostieDetailsPage, 151 | // HostieEditPage, 152 | // HostieListPage, 153 | // LoginPage, 154 | // LogoutPage, 155 | // SettingPage, 156 | // StatusPage, 157 | // UnlockPage, 158 | // WelcomePage, 159 | ], 160 | imports: [ 161 | 162 | // << Page Modules >> 163 | 164 | AboutPageModule, 165 | BrowserModule, 166 | BotieCreatePageModule, 167 | BotieDetailsPageModule, 168 | BotieListPageModule, 169 | DashboardPageModule, 170 | FeedbackPageModule, 171 | GiftieListPageModule, 172 | HelpPageModule, 173 | HostieCreatePageModule, 174 | HostieDetailsPageModule, 175 | HostieEditPageModule, 176 | HostieListPageModule, 177 | LoginPageModule, 178 | LogoutPageModule, 179 | SettingPageModule, 180 | StatusPageModule, 181 | UnlockPageModule, 182 | WelcomePageModule, 183 | 184 | // Others 185 | 186 | AuthModule.forRoot(), 187 | DbModule.forRoot(), 188 | HttpClientModule, 189 | IonicModule.forRoot(ChatieApp), 190 | IonicStorageModule.forRoot(), 191 | // WechatyModule, 192 | ], 193 | bootstrap: [IonicApp], 194 | entryComponents: [ 195 | ChatieApp, 196 | // << Pages >> 197 | // BotieDetailsPage, 198 | // BotieListPage, 199 | // DashboardPage, 200 | // FeedbackPage, 201 | // GiftieListPage, 202 | // HelpPage, 203 | // HostieCreatePage, 204 | // HostieDetailsPage, 205 | // HostieEditPage, 206 | // HostieListPage, 207 | // LoginPage, 208 | // LogoutPage, 209 | // SettingPage, 210 | // StatusPage, 211 | // UnlockPage, 212 | // WelcomePage, 213 | ], 214 | providers: [ 215 | IonicErrorHandler, 216 | StatusBar, 217 | SplashScreen, 218 | { 219 | provide: Brolog, 220 | useValue: log, 221 | }, 222 | { 223 | provide: ErrorHandler, 224 | useClass: ProErrorHandler, 225 | }, 226 | { 227 | provide: APP_INITIALIZER, 228 | useFactory: appInitializerFactory, 229 | deps: [], 230 | multi: true, 231 | }, 232 | ], 233 | }) 234 | 235 | export class AppModule {} 236 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016-2018 Huan LI 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CHATIE APP 2 | 3 | 4 | 5 | Chatie App for Android & Ios & Web 6 | 7 | Chatie.io - Make your own ChatBot in no language, in minutes. 8 | 9 | ![Powered by Ionic & Angular](https://chatie.github.io/app/images/ionic-angular-bot.jpg "Bot is Powered by Ionic & Angular") 10 | > Picture Credit: 11 | 12 | ## FEATURES 13 | 14 | * Passwordless Auth 15 | * Serverless Deploy 16 | * Languageless Programing 17 | 18 | ## DEVELOPMENT 19 | 20 | ### Linux 21 | 22 | OS: Ubuntu 17.10 23 | 24 | #### Install Android SDK 25 | 26 | ```shell 27 | sudo apt install \ 28 | adb \ 29 | android-platform-tools-base \ 30 | android-sdk \ 31 | android-sdk-platform-23 \ 32 | android-sdk \ 33 | android-sdk-platform-tools 34 | 35 | cd $ANDROID_HOME 36 | # download link comes from https://developer.android.com/studio/index.html 37 | wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip 38 | sudo unzip -n sdk-tools-linux-3859397.zip 39 | sudo chmod +x \ 40 | tools/bin/sdkmanager \ 41 | tools/bin/avdmanager 42 | 43 | # XXX: make sure the following line is required...? 44 | sudo ./tools/bin/sdkmanager "build-tools;27.0.3" 45 | 46 | # IMPORTANT: Accept all the licenses 47 | ./tools/bin/sdkmanager --licenses 48 | 49 | ``` 50 | 51 | #### Build Android APK 52 | 53 | ```shell 54 | ./scripts/android.sh 55 | ``` 56 | 57 | ### Mac 58 | 59 | #### Install ... 60 | 61 | ```shell 62 | # Ios: https://cocoapods.org/ 63 | $ sudo gem install cocoapods 64 | ``` 65 | 66 | #### Build iOS IPA 67 | 68 | ```shell 69 | ./scripts/ios.sh 70 | ``` 71 | 72 | ### Browser 73 | 74 | #### Build 75 | 76 | ```shell 77 | ionic cordova platform add browser 78 | ionic cordova build browser --prod 79 | ``` 80 | 81 | ## APP PUBLISHING 82 | 83 | ### Google Play 84 | 85 | * 86 | 87 | ### Apple Store 88 | 89 | * 90 | * 91 | 92 | 1. Xcode -> Product -> 93 | 1. Build 94 | 1. Archive 95 | 96 | ### Web 97 | 98 | #### Develop 99 | 100 | #### Debug Switch 101 | 102 | By adding `?BROLOG_LEVEL=silly` to URL, you can enable full debug output messages in the console. 103 | 104 | For example: 105 | 106 | * https://localhost:8100/?BROLOG_LEVEL=silly 107 | * https://app.chatie.io/?BROLOG_LEVEL=silly 108 | 109 | ### Learning Resources 110 | 111 | #### Copy Cat Clone 112 | 113 | * [Build a WhatsApp Clone with Ionic 2, Angular 2, and Meteor!](https://blog.ionicframework.com/build-a-whatsapp-clone-with-ionic-2-angular-2-and-meteor/) 114 | 115 | #### Angular 116 | 117 | * [Angular Package Format v6.0](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview), design document at Google Docs 118 | * [How to Build an Angular 5 Material App](https://coursetro.com/posts/code/113/How-to-Build-an-Angular-5-Material-App) 119 | 120 | #### Material 121 | 122 | * [Angular Material And Angular 6 — Material Design For Angular](https://medium.com/codingthesmartway-com-blog/angular-material-and-angular-6-material-design-for-angular-6b1a3ee476f0) 123 | 124 | #### Auth0 125 | 126 | * [OAuth 2.0](https://auth0.com/docs/protocols/oauth2) 127 | * [Silent Authentication](https://auth0.com/docs/api-auth/tutorials/silent-authentication) 128 | * [Doc for Get User Info](https://auth0.com/docs/api/authentication#get-user-info) 129 | * [Calling your APIs with Auth0 tokens](https://auth0.com/docs/api-auth/tutorials/adoption/api-tokens) 130 | * [User profile claims and scope](https://auth0.com/docs/api-auth/tutorials/adoption/scope-custom-claims) 131 | * [Tokens used by Auth0](https://auth0.com/docs/tokens) 132 | * [What is the right setup for Lock + SPA + Native + Rest API?](https://community.auth0.com/t/what-is-the-right-setup-for-lock-spa-native-rest-api/6316/2) 133 | * [Auth0 Ionic2 Quick Start](https://auth0.com/docs/quickstart/native/ionic2) 134 | * [Ionic 2 and Auth0(outdated: v0.9)](http://blog.ionic.io/ionic-2-and-auth0/) 135 | * [Experimenting With Auth0 Passwordless Email Authentication In Angular 2.4.1](https://www.bennadel.com/blog/3207-experimenting-with-auth0-passwordless-email-authentication-in-angular-2-4-1.htm) 136 | * [OpenID Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) 137 | * [Server + API Architecture Scenario](https://auth0.com/docs/architecture-scenarios/application/server-api) 138 | * [Verify Access Tokens](https://auth0.com/docs/api-auth/tutorials/verify-access-token) 139 | * [Auth0 + Scaphold](https://scaphold.io/community/questions/scaphold-social-login/) 140 | 141 | <<<<<<< HEAD 142 | ### RxJS 143 | ======= 144 | #### Rxjs 145 | >>>>>>> ec6237233f54b2f152977a6b8964732ce6f054c4 146 | 147 | How to use Observable. 148 | 149 | * [Observables for ECMAScript](https://github.com/tc39/proposal-observable) 150 | * [Migrating from RxJS 4 to 5](https://github.com/ReactiveX/rxjs/blob/master/MIGRATION.md) 151 | 152 | ##### Build from Scratch 153 | 154 | * [Creating Observable From Scratch(Video)](https://egghead.io/lessons/rxjs-creating-observable-from-scratch) 155 | * [Learning Observable By Building Observable](https://medium.com/@benlesh/learning-observable-by-building-observable-d5da57405d87) 156 | 157 | #### Others 158 | 159 | * [Functional Programming in Javascript](http://reactivex.io/learnrx/) 160 | * [How to build Angular 2 apps using Observable Data Services - Pitfalls to avoid](http://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/) 161 | * [3 Common Rxjs Pitfalls that you might find while building Angular 2 Applications](http://blog.angular-university.io/angular-2-rxjs-common-pitfalls/) 162 | * [10 Need-to-Know RxJS Functions with Examples](https://www.sitepoint.com/rxjs-functions-with-examples/) 163 | * [Asynchronous Programming at Netflix - @Scale 2014 - Web](https://www.youtube.com/watch?v=gawmdhCNy-A) 164 | * [RxJS, the smartest dumbest tool ever](https://www.christianalfoni.com/articles/2016_03_31_RxJs-the-smartest-dumbest-tool-ever) 165 | * [COLD VS HOT OBSERVABLES](https://blog.thoughtram.io/angular/2016/06/16/cold-vs-hot-observables.html#caveat-http-with-observables) 166 | * [The Difference Between Throttling and Debouncing](https://css-tricks.com/the-difference-between-throttling-and-debouncing/) 167 | * [RxJS: Don’t Unsubscribe](https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87) 168 | 169 | #### Machine Learning 170 | 171 | * [architecture-free neural network library for node.js and the browser](http://caza.la/synaptic) 172 | 173 | ### Progressive Web Apps 174 | 175 | A Progressive Web App(PWA) uses modern web capabilities to deliver an app-like user experience. 176 | 177 | * [Ionic PWA Doc](http://ionicframework.com/docs/v2/resources/progressive-web-apps/) 178 | * [Ionic PWA Blog](http://blog.ionic.io/navigating-the-world-of-progressive-web-apps-with-ionic-2/) 179 | * [Creating (near) native mobile web app (aka progressive web app) for iOS with Ionic 2](https://technology.amis.nl/2016/08/16/creating-near-native-mobile-web-app-aka-progressive-web-app-for-ios-with-ionic-2/) 180 | 181 | ### Database 182 | 183 | #### 1. GraphQL 184 | 185 | * [Zero to GraphQL in 30 Minutes – Steven Luscher](https://www.youtube.com/watch?v=UBGzsb2UkeY) 186 | * [New features in GraphQL: Batch, defer, stream, live, and subscribe](https://dev-blog.apollodata.com/new-features-in-graphql-batch-defer-stream-live-and-subscribe-7585d0c28b07#.tzc669fjv) 187 | * [GraphQL(with Subscription) Backend As A Service](https://scaphold.io/) 188 | 189 | #### 2. PouchDB 190 | 191 | * [Offline Syncing in Ionic 2 with PouchDB & CouchDB](https://www.joshmorony.com/offline-syncing-in-ionic-2-with-pouchdb-couchdb/) 192 | 193 | ### Testing 194 | 195 | * [Writing Marble Tests](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md) 196 | * [Introduction to RxJS Marble Testing](https://egghead.io/lessons/rxjs-introduction-to-rxjs-marble-testing) 197 | 198 | ### Open API Specification 199 | 200 | * [Top Specification Formats for REST APIs](http://nordicapis.com/top-specification-formats-for-rest-apis/) 201 | * [Speed up your RESTful API development in Node.js with Swagger](https://scotch.io/tutorials/speed-up-your-restful-api-development-in-node-js-with-swagger) 202 | * [Medium.com API Specification](https://github.com/amardeshbd/medium-api-specification) 203 | * [Writing OpenAPI (Swagger) Specification Tutorial](https://apihandyman.io/writing-openapi-swagger-specification-tutorial-part-1-introduction/) 204 | 205 | ### Push 206 | 207 | * [PhoneGap Day US Push Workshop 2016 Build a Hybrid Mobile App with Push](http://macdonst.github.io/push-workshop/index.html) 208 | 209 | ### UI 210 | 211 | * https://vex.visurel.com/ 212 | 213 | ## TODO 214 | 215 | 1. [Use Lighthouse to improve quality](https://developers.google.com/web/tools/lighthouse/) 216 | 1. GitHub: 217 | 1. Try: 218 | 219 | * [x] Hostie Event Page 220 | * [ ] Hostie QR Code Push 221 | * [ ] Store Publish 222 | * [ ] Other 223 | * [ ] Feedback enable Wilddog & Scrollglue 224 | * [ ] Gavatar in menu with Matrix CSS 225 | 226 | ## SEE ALSO 227 | 228 | * [Motion.AI - Chatbots made easy.](https://www.motion.ai) 229 | * [Static web publishing for Front-End Developers](https://surge.sh) 230 | 231 | ## CHANGELOG 232 | 233 | ### v0.3 (master) Apr, 2018 234 | 235 | 1. Promote `Auth` service as a solo npm package [auth-angular](https://github.com/zixia/auth-angular) 236 | 237 | ### v0.2 Feb, 2018 238 | 239 | 1. Upgrade to Ionic 3 & Angular 5 240 | 1. GraphQL DB Integration 241 | 242 | ### v0.1 Apri, 2017 243 | 244 | 1. Auth0 Integration 245 | 1. FastLane.tools Integration 246 | 1. Ionic DB Integration 247 | 248 | ### v0.0 Dec 16, 2016 249 | 250 | 1. Build with Ionic/Angular 2 251 | 1. Prototype with Ionic Creator 252 | 253 | ## AUTHOR 254 | 255 | [Huan LI](http://linkedin.com/in/zixia) \ 256 | 257 | 258 | profile for zixia on Stack Exchange, a network of free, community-driven Q&A sites 259 | 260 | 261 | ## COPYRIGHT & LICENSE 262 | 263 | * Code & Docs © 2017-2018 Huan LI \ 264 | * Code released under the Apache-2.0 License 265 | * Docs released under Creative Commons 266 | 267 | ## MEMO 268 | 269 | title: "Welcome to the Platform!", 270 | description: "The Ionic Component Documentation showcases a number of useful components that are included out of the box with Ionic.", 271 | 272 | title: "What is platform?", 273 | description: "Ionic Framework is an open source SDK that enables developers to build high quality mobile apps with web technologies like HTML, CSS, and JavaScript.", 274 | 275 | title: "What isPlatform?", 276 | description: "The Ionic Platform is a cloud platform for managing and scaling Ionic apps with integrated services like push notifications, native builds, user auth, and live updating.", 277 | 278 | ## ALPHA DESIGN 279 | 280 | ```ts 281 | import { Giftie } from '@chatie/giftie' 282 | 283 | ``` 284 | 285 | ```ts 286 | import { Botie } from '@chatie/botie' 287 | 288 | const botie = new Botie() 289 | 290 | ``` 291 | 292 | ```ts 293 | const botie = new Botie('token') 294 | 295 | try { 296 | await botie.init() 297 | await botie.test().pipe( 298 | tap(progress => { 299 | console.log('on progress...', progress) 300 | }) 301 | ).toPromise() 302 | wechaty.addBotie(botie) 303 | } catch (e) { 304 | if ( e.instanceof(UnitTestingException )) { 305 | // botie test fail 306 | } else { 307 | // other exception 308 | } 309 | } 310 | 311 | function addBotie(botie: Botie): Promise { 312 | // const eventNameList = botie.eventNameList() 313 | // const eventListenerList = botie.eventListenerList() 314 | 315 | // for (const i in eventNameList) { 316 | // this.addListener(eventNameList[i], eventListenerList[i]) 317 | // } 318 | 319 | botie.bind(this) 320 | 321 | botie.listener.subscribe.pipe( 322 | tap( 323 | (event, listener) => this.addListener(event, listener), 324 | ), 325 | ).toPromise() 326 | } 327 | 328 | function removeBotie(botie: Botie) Promise { 329 | botie.listener.subscribe.pipe( 330 | tap( 331 | (event, listener) => this.removeListener(event, listener), 332 | ), 333 | ).toPromise() 334 | } 335 | 336 | const wechaty = Wechaty.instance() 337 | wechaty.setPuppet(new HostiePuppet('token')) 338 | wechaty.start() 339 | 340 | ``` 341 | --------------------------------------------------------------------------------