├── app ├── vendor-platform.ts ├── vendor-platform.ios.ts ├── models │ ├── index.ts │ └── Task.ts ├── store │ ├── reducers │ │ ├── index.ts │ │ └── task.reducer.ts │ ├── states │ │ ├── index.ts │ │ └── task.state.ts │ ├── store.module.ts │ ├── app.state.ts │ ├── effects │ │ └── task.effects.ts │ ├── actions │ │ └── task.actions.ts │ └── services │ │ └── task.service.ts ├── +task │ ├── components │ │ ├── task-banner │ │ │ ├── task-banner.component.css │ │ │ ├── task-banner.component.android.css │ │ │ ├── task-banner.component.html │ │ │ ├── task-banner.common.css │ │ │ └── task-banner.component.ts │ │ ├── task-form │ │ │ ├── task-form.component.android.css │ │ │ ├── task-form.component.ios.css │ │ │ ├── task-form.common.css │ │ │ ├── task-form.component.html │ │ │ └── task-form.component.ts │ │ ├── task-count │ │ │ ├── task-count.component.html │ │ │ ├── task-count.component.css │ │ │ ├── task-count.component.android.css │ │ │ └── task-count.component.ts │ │ └── task-list │ │ │ ├── task-list.component.css │ │ │ ├── task-list.component.html │ │ │ └── task-list.component.ts │ ├── task.routing.ts │ └── task.module.ts ├── App_Resources │ ├── iOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── icon-29.png │ │ │ │ ├── icon-40.png │ │ │ │ ├── icon-76.png │ │ │ │ ├── icon-29@2x.png │ │ │ │ ├── icon-29@3x.png │ │ │ │ ├── icon-40@2x.png │ │ │ │ ├── icon-40@3x.png │ │ │ │ ├── icon-60@2x.png │ │ │ │ ├── icon-60@3x.png │ │ │ │ ├── icon-76@2x.png │ │ │ │ ├── icon-83.5@2x.png │ │ │ │ └── Contents.json │ │ │ ├── LaunchImage.launchimage │ │ │ │ ├── Default.png │ │ │ │ ├── Default@2x.png │ │ │ │ ├── Default-568h@2x.png │ │ │ │ ├── Default-667h@2x.png │ │ │ │ ├── Default-736h@3x.png │ │ │ │ ├── Default-Landscape.png │ │ │ │ ├── Default-Portrait.png │ │ │ │ ├── Default-Landscape@2x.png │ │ │ │ ├── Default-Landscape@3x.png │ │ │ │ ├── Default-Portrait@2x.png │ │ │ │ └── Contents.json │ │ │ ├── LaunchScreen.Center.imageset │ │ │ │ ├── LaunchScreen-Center.png │ │ │ │ ├── LaunchScreen-Center@2x.png │ │ │ │ └── Contents.json │ │ │ └── LaunchScreen.AspectFill.imageset │ │ │ │ ├── LaunchScreen-AspectFill.png │ │ │ │ ├── LaunchScreen-AspectFill@2x.png │ │ │ │ └── Contents.json │ │ ├── app_bg_day.jpg │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ ├── tasks_complete_night.png │ │ ├── build.xcconfig │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ └── Android │ │ ├── values-v21 │ │ ├── colors.xml │ │ └── styles.xml │ │ ├── drawable-hdpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── background.png │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-ldpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── background.png │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-mdpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── background.png │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-xhdpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── background.png │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-xxhdpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── background.png │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-xxxhdpi │ │ ├── icon.png │ │ ├── logo.png │ │ ├── app_bg_day.jpg │ │ ├── app_bg_night.jpg │ │ ├── background.png │ │ ├── tasks_complete_day.png │ │ └── tasks_complete_night.png │ │ ├── drawable-nodpi │ │ ├── app_bg_day.jpg │ │ ├── app_bg_night.jpg │ │ ├── tasks_complete_day.png │ │ ├── tasks_complete_night.png │ │ └── splash_screen.xml │ │ ├── values │ │ ├── colors.xml │ │ └── styles.xml │ │ ├── app.gradle │ │ └── AndroidManifest.xml ├── app.component.html ├── package.json ├── utils │ ├── Date.ts │ └── Guid.ts ├── main.aot.ts ├── vendor.ts ├── vendor-platform.android.ts ├── app.css ├── app.routing.ts ├── operators.ts ├── main.ts ├── app.component.ts └── app.module.ts ├── .gitignore ├── .vscode └── settings.json ├── tsconfig.json ├── tsconfig.aot.json ├── package.json ├── README.md └── webpack.config.js /app/vendor-platform.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/vendor-platform.ios.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Task'; 2 | -------------------------------------------------------------------------------- /app/store/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './task.reducer'; 2 | -------------------------------------------------------------------------------- /app/store/states/index.ts: -------------------------------------------------------------------------------- 1 | export * from './task.state'; 2 | -------------------------------------------------------------------------------- /app/+task/components/task-banner/task-banner.component.css: -------------------------------------------------------------------------------- 1 | @import 'task-banner.common.css'; 2 | -------------------------------------------------------------------------------- /app/+task/components/task-form/task-form.component.android.css: -------------------------------------------------------------------------------- 1 | @import 'task-form.common.css'; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hooks/ 2 | node_modules/ 3 | platforms/ 4 | report/ 5 | .nsbuildinfo 6 | app/**/*.js 7 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /app/App_Resources/iOS/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/iOS/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/app_bg_night.jpg -------------------------------------------------------------------------------- /app/+task/components/task-banner/task-banner.component.android.css: -------------------------------------------------------------------------------- 1 | @import 'task-banner.common.css'; 2 | 3 | .banner { 4 | margin-top: 20; 5 | } 6 | -------------------------------------------------------------------------------- /app/+task/components/task-form/task-form.component.ios.css: -------------------------------------------------------------------------------- 1 | @import 'task-form.common.css'; 2 | 3 | .action-bar { 4 | padding-bottom: 20; 5 | } 6 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/values-v21/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3d5afe 4 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/icon.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/background.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/background.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/background.png -------------------------------------------------------------------------------- /app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-nodpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-nodpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/background.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/background.png -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "android": { 3 | "v8Flags": "--expose_gc" 4 | }, 5 | "main": "main.js", 6 | "name": "tns-template-hello-world-ng", 7 | "version": "3.1.2" 8 | } -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-nodpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-nodpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/app_bg_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/app_bg_day.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/app_bg_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/app_bg_night.jpg -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/background.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-hdpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-hdpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-ldpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-ldpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-mdpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-mdpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-nodpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-nodpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-nodpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-nodpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xhdpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xhdpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxhdpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxhdpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/tasks_complete_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/tasks_complete_day.png -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-xxxhdpi/tasks_complete_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/Android/drawable-xxxhdpi/tasks_complete_night.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /app/utils/Date.ts: -------------------------------------------------------------------------------- 1 | 2 | export class NSDate { 3 | 4 | static isDayTime(today = new Date()): boolean { 5 | const hours = today.getHours(); 6 | return hours > 8 && hours < 17; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/+task/components/task-count/task-count.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sean-perkins/nativescript-task-app/HEAD/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png -------------------------------------------------------------------------------- /app/+task/components/task-count/task-count.component.css: -------------------------------------------------------------------------------- 1 | .count { 2 | padding-bottom: 20; 3 | font-size: 22; 4 | text-align: center; 5 | } 6 | 7 | .count-label { 8 | text-align: center; 9 | padding-top: 20; 10 | font-size: 12; 11 | color: #9b8e94; 12 | } 13 | -------------------------------------------------------------------------------- /app/App_Resources/Android/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #F5F5F5 4 | #757575 5 | #33B5E5 6 | #272734 7 | -------------------------------------------------------------------------------- /app/utils/Guid.ts: -------------------------------------------------------------------------------- 1 | export class Guid { 2 | 3 | static next() { 4 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 5 | const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 6 | return v.toString(16); 7 | }); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/+task/components/task-count/task-count.component.android.css: -------------------------------------------------------------------------------- 1 | .count { 2 | padding-bottom: 10; 3 | font-size: 20; 4 | margin-top:5; 5 | text-align: center; 6 | } 7 | 8 | .count-label { 9 | text-align: center; 10 | padding-top: 25; 11 | margin-top:5; 12 | font-size: 10; 13 | color: #9b8e94; 14 | } 15 | -------------------------------------------------------------------------------- /app/main.aot.ts: -------------------------------------------------------------------------------- 1 | // this import should be first in order to load some required settings (like globals and reflect-metadata) 2 | import { platformNativeScript } from "nativescript-angular/platform-static"; 3 | 4 | import { AppModuleNgFactory } from "./app.module.ngfactory"; 5 | 6 | platformNativeScript().bootstrapModuleFactory(AppModuleNgFactory); 7 | -------------------------------------------------------------------------------- /app/+task/components/task-banner/task-banner.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /app/App_Resources/Android/drawable-nodpi/splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/vendor.ts: -------------------------------------------------------------------------------- 1 | require("./vendor-platform"); 2 | 3 | require("reflect-metadata"); 4 | require("@angular/platform-browser"); 5 | require("@angular/core"); 6 | require("@angular/common"); 7 | require("@angular/forms"); 8 | require("@angular/http"); 9 | require("@angular/router"); 10 | 11 | require("nativescript-angular/platform-static"); 12 | require("nativescript-angular/forms"); 13 | require("nativescript-angular/router"); 14 | -------------------------------------------------------------------------------- /app/vendor-platform.android.ts: -------------------------------------------------------------------------------- 1 | require("application"); 2 | if (!global["__snapshot"]) { 3 | /* 4 | In case snapshot generation is enabled these modules will get into the bundle but will not be required/evaluated. 5 | The snapshot webpack plugin will add them to the tns-java-classes.js bundle file. This way, they will be evaluated on app start as early as possible. 6 | */ 7 | require("ui/frame"); 8 | require("ui/frame/activity"); 9 | } 10 | -------------------------------------------------------------------------------- /app/App_Resources/Android/app.gradle: -------------------------------------------------------------------------------- 1 | // Add your native dependencies here: 2 | 3 | // Uncomment to add recyclerview-v7 dependency 4 | //dependencies { 5 | // compile 'com.android.support:recyclerview-v7:+' 6 | //} 7 | 8 | android { 9 | defaultConfig { 10 | generatedDensities = [] 11 | applicationId = "org.nativescript.nativescripteventapp" 12 | } 13 | aaptOptions { 14 | additionalParameters "--no-version-vectors" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchScreen-Center.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchScreen-Center@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchScreen-AspectFill.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchScreen-AspectFill@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /app/App_Resources/iOS/build.xcconfig: -------------------------------------------------------------------------------- 1 | // You can add custom settings here 2 | // for example you can uncomment the following line to force distribution code signing 3 | // CODE_SIGN_IDENTITY = iPhone Distribution 4 | // To build for device with Xcode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html 5 | // DEVELOPMENT_TEAM = YOUR_TEAM_ID; 6 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 7 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 8 | -------------------------------------------------------------------------------- /app/store/store.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { HttpModule } from '@angular/http'; 3 | import { TaskService } from './services/task.service'; 4 | import { EffectsModule } from '@ngrx/effects'; 5 | import { TaskEffects } from './effects/task.effects'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | HttpModule, 10 | EffectsModule.run(TaskEffects) 11 | ], 12 | providers: [ 13 | TaskService 14 | ], 15 | schemas: [NO_ERRORS_SCHEMA] 16 | }) 17 | export class AppStoreModule { } 18 | -------------------------------------------------------------------------------- /app/app.css: -------------------------------------------------------------------------------- 1 | /* 2 | In NativeScript, the app.css file is where you place CSS rules that 3 | you would like to apply to your entire application. Check out 4 | http://docs.nativescript.org/ui/styling for a full list of the CSS 5 | selectors and properties you can use to style UI components. 6 | 7 | /* 8 | In many cases you may want to use the NativeScript core theme instead 9 | of writing your own CSS rules. For a full list of class names in the theme 10 | refer to http://docs.nativescript.org/ui/theme. 11 | */ 12 | @import 'nativescript-theme-core/css/core.light.css'; 13 | -------------------------------------------------------------------------------- /app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { NativeScriptRouterModule } from 'nativescript-angular/router'; 3 | import { Routes } from '@angular/router'; 4 | 5 | const routes: Routes = [ 6 | { path: '', redirectTo: '/tasks', pathMatch: 'full' }, 7 | { 8 | path: 'tasks', 9 | loadChildren: './+task/task.module#TaskModule' 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [NativeScriptRouterModule.forRoot(routes)], 15 | exports: [NativeScriptRouterModule] 16 | }) 17 | export class AppRoutingModule { } 18 | -------------------------------------------------------------------------------- /app/store/app.state.ts: -------------------------------------------------------------------------------- 1 | import { ActionReducer } from '@ngrx/store'; 2 | import { compose } from '@ngrx/core/compose'; 3 | import { combineReducers } from '@ngrx/store'; 4 | import * as appReducers from './reducers/index'; 5 | import * as appStates from './states/index'; 6 | 7 | export interface IAppState { 8 | tasks: appStates.TaskState; 9 | }; 10 | 11 | const reducers = { 12 | tasks: appReducers.taskReducer 13 | }; 14 | 15 | export function AppReducer(state: any, action: any) { 16 | return combineReducers(reducers)(state, action); 17 | } 18 | 19 | export * from './states/index'; 20 | -------------------------------------------------------------------------------- /app/+task/task.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NativeScriptRouterModule } from 'nativescript-angular/router'; 3 | import { Routes } from '@angular/router'; 4 | import { TaskListComponent } from './components/task-list/task-list.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', pathMatch: 'full', component: TaskListComponent }, 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [ 12 | NativeScriptRouterModule.forChild(routes) 13 | ], 14 | exports: [ 15 | NativeScriptRouterModule 16 | ] 17 | }) 18 | export class TaskRoutingModule { } 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.watcherExclude": { 3 | "hooks/*/**": true, 4 | "node_modules/*/**": true, 5 | "platforms/*/**": true 6 | }, 7 | "search.exclude": { 8 | "platforms": true, 9 | "node_modules": true, 10 | "hooks": true 11 | }, 12 | "[typescript]": { 13 | "editor.formatOnSave": true 14 | }, 15 | "files.trimTrailingWhitespace": true, 16 | "files.insertFinalNewline": true, 17 | "editor.trimAutoWhitespace": true, 18 | "editor.formatOnPaste": true, 19 | "autoimport.filesToScan": "src/app/*.{ts,tsx}" 20 | } 21 | -------------------------------------------------------------------------------- /app/operators.ts: -------------------------------------------------------------------------------- 1 | // rxjs 2 | import 'rxjs/add/observable/throw'; 3 | import 'rxjs/add/operator/map'; 4 | import 'rxjs/add/operator/do'; 5 | import 'rxjs/add/operator/startWith'; 6 | import 'rxjs/add/operator/switchMap'; 7 | import 'rxjs/add/operator/mergeMap'; 8 | import 'rxjs/add/operator/combineLatest'; 9 | import 'rxjs/add/operator/catch'; 10 | import 'rxjs/add/operator/take'; 11 | import 'rxjs/add/operator/let'; 12 | import 'rxjs/add/operator/withLatestFrom'; 13 | import 'rxjs/add/operator/takeUntil'; 14 | import 'rxjs/add/observable/combineLatest'; 15 | import 'rxjs/add/observable/fromPromise'; 16 | 17 | // ngrx 18 | import '@ngrx/core/add/operator/select'; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "experimentalDecorators": true, 6 | "emitDecoratorMetadata": true, 7 | "noEmitHelpers": true, 8 | "noEmitOnError": true, 9 | "lib": [ 10 | "es6", 11 | "dom", 12 | "es2015.iterable" 13 | ], 14 | "baseUrl": ".", 15 | "paths": { 16 | "*": [ 17 | "./node_modules/tns-core-modules/*", 18 | "./node_modules/*" 19 | ] 20 | } 21 | }, 22 | "exclude": [ 23 | "node_modules", 24 | "platforms", 25 | "**/*.aot.ts" 26 | ] 27 | } -------------------------------------------------------------------------------- /app/main.ts: -------------------------------------------------------------------------------- 1 | // this import should be first in order to load some required settings (like globals and reflect-metadata) 2 | import { platformNativeScriptDynamic } from "nativescript-angular/platform"; 3 | 4 | import { AppModule } from "./app.module"; 5 | 6 | // A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page. 7 | // Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers. 8 | // A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic, 9 | // that sets up a NativeScript application and can bootstrap the Angular framework. 10 | platformNativeScriptDynamic().bootstrapModule(AppModule); 11 | -------------------------------------------------------------------------------- /app/+task/components/task-count/task-count.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | /** 3 | * Task Count Component 4 | * Displays a styled number with a label below it 5 | */ 6 | @Component({ 7 | moduleId: module.id, 8 | selector: 'ns-task-count', 9 | templateUrl: './task-count.component.html', 10 | styleUrls: ['./task-count.component.css'] 11 | }) 12 | export class TaskCountComponent { 13 | // The number of items 14 | @Input() count: number; 15 | // The description of the type of number being displayed (i.e. All) 16 | @Input() label: string; 17 | 18 | @Output() onPress: EventEmitter = new EventEmitter(); 19 | // Bind to the NS input to control display of this element on the parent view 20 | @Input() col = 0; 21 | 22 | press(): void { 23 | this.onPress.emit(true); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/+task/components/task-banner/task-banner.common.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | background-repeat: no-repeat; 3 | background-position: center; 4 | background-size: cover; 5 | } 6 | 7 | .banner .title { 8 | horizontal-align: right; 9 | text-align: right; 10 | vertical-align: top; 11 | color: #fff; 12 | width: 100%; 13 | font-size: 22; 14 | font-weight: bold; 15 | padding: 20; 16 | } 17 | 18 | .banner .today { 19 | horizontal-align: right; 20 | text-align: right; 21 | vertical-align: top; 22 | color: #fff; 23 | width: 100%; 24 | font-size: 15; 25 | padding: 20; 26 | margin-top: 30; 27 | } 28 | 29 | .banner .btn-clear { 30 | horizontal-align: left; 31 | text-align: left; 32 | vertical-align: bottom; 33 | padding: 10 20; 34 | font-size: 10; 35 | font-weight: bold; 36 | color: #fff; 37 | text-transform: uppercase; 38 | background-color: transparent; 39 | border-color: transparent; 40 | border-width: 1; 41 | } 42 | -------------------------------------------------------------------------------- /app/store/reducers/task.reducer.ts: -------------------------------------------------------------------------------- 1 | import { TaskState } from './../states/task.state'; 2 | import { Action } from '@ngrx/store'; 3 | 4 | export function taskReducer( 5 | state: TaskState = new TaskState, 6 | action: Action) { 7 | switch (action.type) { 8 | case TaskState.ActionTypes.CLEAR: 9 | return Object.assign({}, state, { 10 | tasks: [] 11 | }); 12 | case TaskState.ActionTypes.FETCH: 13 | return Object.assign({}, state, { 14 | loading: true 15 | }); 16 | case TaskState.ActionTypes.FETCH_SUCCESS: 17 | return Object.assign({}, state, { 18 | tasks: action.payload, 19 | loading: false 20 | }); 21 | case TaskState.ActionTypes.FETCH_FAILED: 22 | return Object.assign({}, state, { 23 | task: [], 24 | loading: false 25 | }); 26 | default: 27 | return state; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/App_Resources/Android/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import './operators'; 3 | import * as app from 'tns-core-modules/application'; 4 | import { NSDate } from './utils/Date'; 5 | import { isAndroid } from 'tns-core-modules/platform'; 6 | 7 | @Component({ 8 | selector: 'ns-app', 9 | templateUrl: 'app.component.html', 10 | encapsulation: ViewEncapsulation.Native 11 | }) 12 | 13 | export class AppComponent { 14 | 15 | constructor() { 16 | app.addCss(this.getStyles(NSDate.isDayTime() ? '#ff9d6e' : '#0061ef')); 17 | } 18 | 19 | private getStyles(accentColor: string): any { 20 | return ` 21 | .count, .input, .textarea { 22 | color: ${accentColor}; 23 | } 24 | .banner { 25 | background-image: url('res://app_bg_${NSDate.isDayTime() ? 'day' : 'night'}'); 26 | } 27 | .action-bar, .btn-delete { 28 | background-color: ${accentColor} 29 | } 30 | .fab { 31 | background-color: ${NSDate.isDayTime() ? '#FF482F' : '#0061ef'}; 32 | border-color: ${NSDate.isDayTime() ? '#ef432c' : '#035bdd'}; 33 | } 34 | `; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NgModuleFactoryLoader, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { NSModuleFactoryLoader } from 'nativescript-angular/router'; 3 | import { NativeScriptModule } from 'nativescript-angular/nativescript.module'; 4 | import { AppRoutingModule } from './app.routing'; 5 | import { AppComponent } from './app.component'; 6 | 7 | import { StoreModule } from '@ngrx/store'; 8 | import { AppStoreModule } from './store/store.module'; 9 | import { AppReducer } from './store/app.state'; 10 | 11 | // Uncomment and add to NgModule imports if you need to use two-way binding 12 | // import { NativeScriptFormsModule } from 'nativescript-angular/forms'; 13 | 14 | // Uncomment and add to NgModule imports if you need to use the HTTP wrapper 15 | // import { NativeScriptHttpModule } from 'nativescript-angular/http'; 16 | 17 | @NgModule({ 18 | bootstrap: [ 19 | AppComponent 20 | ], 21 | imports: [ 22 | NativeScriptModule, 23 | AppRoutingModule, 24 | AppStoreModule, 25 | StoreModule.provideStore(AppReducer), 26 | ], 27 | declarations: [ 28 | AppComponent 29 | ], 30 | providers: [ 31 | { provide: NgModuleFactoryLoader, useClass: NSModuleFactoryLoader }, 32 | ], 33 | schemas: [ 34 | NO_ERRORS_SCHEMA 35 | ] 36 | }) 37 | export class AppModule { } 38 | -------------------------------------------------------------------------------- /app/+task/components/task-form/task-form.common.css: -------------------------------------------------------------------------------- 1 | .action-bar { 2 | orientation: horizontal; 3 | margin-top: 20; 4 | } 5 | 6 | .night .action-bar { 7 | background-color: #0061ef; 8 | } 9 | 10 | .form-container { 11 | background-color: #fff; 12 | padding: 20; 13 | } 14 | 15 | .btn-cancel { 16 | text-align: left; 17 | width: 50%; 18 | padding-left: 20; 19 | color: #fff; 20 | background-color: transparent; 21 | border-color: transparent; 22 | border-width: 1; 23 | text-transform: capitalize; 24 | } 25 | 26 | .btn-save { 27 | text-align: right; 28 | width: 50%; 29 | padding-right: 20; 30 | color: #fff; 31 | font-weight: bold; 32 | border-color: transparent; 33 | border-width: 1; 34 | background-color: transparent; 35 | text-transform: capitalize; 36 | } 37 | 38 | .label { 39 | font-size: 12; 40 | color: #9b8e94; 41 | } 42 | 43 | .createdAt { 44 | border-top-width: 1; 45 | border-top-color: #FBF8F8; 46 | padding-top: 10; 47 | } 48 | 49 | .label.error { 50 | color: #ff9d6e; 51 | } 52 | 53 | .night .label.error { 54 | color: #0061ef; 55 | } 56 | 57 | .input { 58 | border-bottom-width: 1; 59 | border-bottom-color: #FBF8F8; 60 | margin-top: 5; 61 | padding-bottom: 5; 62 | font-size: 13; 63 | } 64 | 65 | .textarea { 66 | height: 60; 67 | } 68 | -------------------------------------------------------------------------------- /app/+task/components/task-banner/task-banner.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { NSDate } from '../../../utils/Date'; 3 | import * as dialogs from 'tns-core-modules/ui/dialogs'; 4 | /** 5 | * Styled Banner Header 6 | * Displays personalized background graphic with todays date and 7 | * an action to clear existing tasks from the app. 8 | */ 9 | @Component({ 10 | moduleId: module.id, 11 | selector: 'ns-task-banner', 12 | templateUrl: './task-banner.component.html', 13 | styleUrls: ['./task-banner.component.css'] 14 | }) 15 | export class TaskBannerComponent { 16 | 17 | /** 18 | * Output event when a Label is tapped 19 | */ 20 | @Output() onClear: EventEmitter = new EventEmitter(); 21 | /** 22 | * Todays' date object 23 | */ 24 | today = new Date(); 25 | 26 | /** 27 | * Returns a different heading label based on if it is daytime or nighttime 28 | */ 29 | get todayLabel(): string { 30 | return NSDate.isDayTime(this.today) ? 'Today' : 'Tonight'; 31 | } 32 | 33 | clearTask(): void { 34 | // Prompts the user to confirm before clearing all tasks 35 | dialogs.confirm('Are you sure you want to clear all tasks?') 36 | .then(confirmed => { 37 | if (confirmed) { 38 | this.onClear.next(true); 39 | } 40 | }); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/+task/task.module.ts: -------------------------------------------------------------------------------- 1 | import { TaskListComponent } from './components/task-list/task-list.component'; 2 | import { TaskFormComponent } from './components/task-form/task-form.component'; 3 | import { TaskCountComponent } from './components/task-count/task-count.component'; 4 | import { TaskBannerComponent } from './components/task-banner/task-banner.component'; 5 | import { TaskRoutingModule } from './task.routing'; 6 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 7 | import { CommonModule } from '@angular/common'; 8 | import { NativeScriptUIListViewModule } from 'nativescript-telerik-ui/listview/angular'; 9 | import { ModalDialogService } from 'nativescript-angular/modal-dialog'; 10 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 11 | import { NativeScriptFormsModule } from 'nativescript-angular/forms'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | TaskRoutingModule, 17 | NativeScriptUIListViewModule, 18 | NativeScriptFormsModule, 19 | FormsModule, 20 | ReactiveFormsModule 21 | ], 22 | declarations: [ 23 | TaskListComponent, 24 | TaskFormComponent, 25 | TaskCountComponent, 26 | TaskBannerComponent 27 | ], 28 | exports: [], 29 | entryComponents: [ 30 | TaskFormComponent 31 | ], 32 | providers: [ 33 | ModalDialogService 34 | ], 35 | schemas: [NO_ERRORS_SCHEMA] 36 | }) 37 | export class TaskModule { } 38 | -------------------------------------------------------------------------------- /app/+task/components/task-list/task-list.component.css: -------------------------------------------------------------------------------- 1 | .placeholder { 2 | height: 100%; 3 | } 4 | 5 | .placeholder Label { 6 | color: #87797f; 7 | text-align: center; 8 | vertical-align: middle; 9 | margin-top: 15; 10 | } 11 | 12 | .placeholder Image { 13 | text-align: center; 14 | vertical-align: center; 15 | width: 72; 16 | height: 72; 17 | } 18 | 19 | .count-bar { 20 | background-color: #fff; 21 | border-bottom-width: 1; 22 | border-bottom-color: #FBF8F8; 23 | } 24 | 25 | .btn-complete { 26 | background-color: #03dd79; 27 | } 28 | 29 | .btn-swipe { 30 | padding: 0 16; 31 | orientation: horizontal; 32 | } 33 | 34 | .btn-swipe Label { 35 | color: #fff; 36 | font-size: 20; 37 | horizontal-align: center; 38 | } 39 | 40 | .fab { 41 | horizontal-align: right; 42 | vertical-align: bottom; 43 | text-align: center; 44 | margin: 0 20 20 0; 45 | font-size: 22; 46 | padding: 0; 47 | border-width: 1; 48 | color: #fff; 49 | border-radius: 50; 50 | width: 50; 51 | height: 50; 52 | } 53 | 54 | .list-item { 55 | padding: 10 15; 56 | background-color: #fff; 57 | border-bottom-width: 1; 58 | border-bottom-color: #FBF8F8; 59 | } 60 | 61 | .list-item.complete Label { 62 | opacity: 0.1; 63 | } 64 | 65 | .list-item .title { 66 | color: #a3979e; 67 | font-weight: 400; 68 | font-size: 18; 69 | } 70 | 71 | .list-item .description { 72 | color: #87797f; 73 | font-size: 14; 74 | } 75 | -------------------------------------------------------------------------------- /app/App_Resources/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/models/Task.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Guid } from '../utils/Guid'; 3 | 4 | export class Task { 5 | /** 6 | * The unique id of the event 7 | */ 8 | id: string; 9 | /** 10 | * The event name 11 | */ 12 | name: string; 13 | /** 14 | * The description of the event 15 | */ 16 | description: string; 17 | /** 18 | * The start time of the event 19 | */ 20 | startTime: any; 21 | /** 22 | * The end time of the event 23 | */ 24 | endTime: any; 25 | /** 26 | * The created timestamp of the event 27 | */ 28 | createdAt: number; 29 | /** 30 | * The updated timestamp of the event 31 | */ 32 | updatedAt: number; 33 | /** 34 | * The timestamp the event was completed 35 | */ 36 | completedAt: number; 37 | 38 | constructor(options: Task = {}) { 39 | this.id = options.id || null; 40 | this.name = options.name || null; 41 | this.description = options.description || null; 42 | this.startTime = options.startTime || null; 43 | this.endTime = options.endTime || null; 44 | this.createdAt = options.createdAt || null; 45 | this.updatedAt = options.updatedAt || null; 46 | this.completedAt = options.completedAt || null; 47 | } 48 | 49 | initalize(): void { 50 | this.id = Guid.next(); 51 | this.createdAt = Date.now(); 52 | this.update(); 53 | } 54 | 55 | update(): void { 56 | this.updatedAt = Date.now(); 57 | } 58 | 59 | complete(): void { 60 | this.completedAt = Date.now(); 61 | } 62 | 63 | get completed(): boolean { 64 | return this.completedAt !== null; 65 | } 66 | 67 | toString = (): string => { 68 | return JSON.stringify(this); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /tsconfig.aot.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "application": ["node_modules/tns-core-modules/application"], 7 | "application-settings": ["node_modules/tns-core-modules/application-settings"], 8 | "camera": ["node_modules/tns-core-modules/camera"], 9 | "color": ["node_modules/tns-core-modules/color"], 10 | "connectivity": ["node_modules/tns-core-modules/connectivity"], 11 | "console": ["node_modules/tns-core-modules/console"], 12 | "data/*": ["node_modules/tns-core-modules/data/*"], 13 | "fetch": ["node_modules/tns-core-modules/fetch"], 14 | "file-system": ["node_modules/tns-core-modules/file-system"], 15 | "fps-meter": ["node_modules/tns-core-modules/fps-meter"], 16 | "globals": ["node_modules/tns-core-modules/globals"], 17 | "http": ["node_modules/tns-core-modules/http"], 18 | "image-asset": ["node_modules/tns-core-modules/image-asset"], 19 | "image-source": ["node_modules/tns-core-modules/image-source"], 20 | "location": ["node_modules/tns-core-modules/location"], 21 | "platform": ["node_modules/tns-core-modules/platform"], 22 | "text": ["node_modules/tns-core-modules/text"], 23 | "timer": ["node_modules/tns-core-modules/timer"], 24 | "trace": ["node_modules/tns-core-modules/trace"], 25 | "ui/*": ["node_modules/tns-core-modules/ui/*"], 26 | "utils/*": ["node_modules/tns-core-modules/utils/*"], 27 | "xhr": ["node_modules/tns-core-modules/xhr"], 28 | "xml": ["node_modules/tns-core-modules/xml"] 29 | }, 30 | "skipLibCheck": true 31 | }, 32 | "exclude": [ 33 | "node_modules", 34 | "platforms" 35 | ], 36 | "angularCompilerOptions": { 37 | "skipMetadataEmit": true, 38 | "genDir": "./" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiresFullScreen 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIStatusBarStyle 47 | UIStatusBarStyleLightContent 48 | UIViewControllerBasedStatusBarAppearance 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/+task/components/task-form/task-form.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 12 | 17 | 18 | 19 | 20 | 23 | 24 | 26 | 29 | 30 | 31 | 33 | 35 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon-29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon-29@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29@3x.png", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "icon-40@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon-40@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon-60@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon-60@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon-29.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "29x29", 53 | "idiom" : "ipad", 54 | "filename" : "icon-29@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon-40.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "40x40", 65 | "idiom" : "ipad", 66 | "filename" : "icon-40@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "icon-76.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "76x76", 77 | "idiom" : "ipad", 78 | "filename" : "icon-76@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "83.5x83.5", 83 | "idiom" : "ipad", 84 | "filename" : "icon-83.5@2x.png", 85 | "scale" : "2x" 86 | } 87 | ], 88 | "info" : { 89 | "version" : 1, 90 | "author" : "xcode" 91 | } 92 | } -------------------------------------------------------------------------------- /app/App_Resources/Android/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | 21 | 22 | 23 | 37 | 38 | 40 | 41 | 42 | 47 | 48 | 50 | 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "NativeScript Application", 3 | "license": "SEE LICENSE IN ", 4 | "readme": "NativeScript Application", 5 | "repository": "", 6 | "nativescript": { 7 | "id": "org.nativescript.nativescripteventapp", 8 | "tns-ios": { 9 | "version": "3.2.0-2017-8-25-1" 10 | }, 11 | "tns-android": { 12 | "version": "3.2.0-next-2017-06-28-1901" 13 | } 14 | }, 15 | "dependencies": { 16 | "@angular/animations": "~4.2.0", 17 | "@angular/common": "~4.2.0", 18 | "@angular/compiler": "~4.2.0", 19 | "@angular/core": "~4.2.0", 20 | "@angular/forms": "~4.2.0", 21 | "@angular/http": "~4.2.0", 22 | "@angular/platform-browser": "~4.2.0", 23 | "@angular/router": "~4.2.0", 24 | "@ngrx/core": "^1.2.0", 25 | "@ngrx/effects": "^2.0.2", 26 | "@ngrx/store": "^2.2.1", 27 | "nativescript-angular": "~4.2.0", 28 | "nativescript-telerik-ui": "^3.0.4", 29 | "nativescript-theme-core": "~1.0.2", 30 | "ngrx-store-freeze": "0.1.9", 31 | "reflect-metadata": "~0.1.8", 32 | "rxjs": "~5.4.2", 33 | "tns-core-modules": "~3.2.0", 34 | "zone.js": "~0.8.2" 35 | }, 36 | "devDependencies": { 37 | "@angular/compiler-cli": "~4.2.0", 38 | "@ngtools/webpack": "~1.5.0", 39 | "babel-traverse": "6.26.0", 40 | "babel-types": "6.26.0", 41 | "babylon": "6.18.0", 42 | "copy-webpack-plugin": "~4.0.1", 43 | "extract-text-webpack-plugin": "~3.0.0", 44 | "lazy": "1.0.11", 45 | "nativescript-css-loader": "~0.26.0", 46 | "nativescript-dev-typescript": "~0.5.0", 47 | "nativescript-dev-webpack": "^0.7.3", 48 | "raw-loader": "~0.5.1", 49 | "resolve-url-loader": "~2.1.0", 50 | "typescript": "~2.4.2", 51 | "webpack": "~3.2.0", 52 | "webpack-bundle-analyzer": "^2.8.2", 53 | "webpack-sources": "~1.0.1" 54 | }, 55 | "scripts": { 56 | "ns-bundle": "ns-bundle", 57 | "publish-ios-bundle": "npm run ns-bundle --ios --publish-app", 58 | "generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install", 59 | "start-android-bundle": "npm run ns-bundle --android --run-app", 60 | "start-ios-bundle": "npm run ns-bundle --ios --run-app", 61 | "build-android-bundle": "npm run ns-bundle --android --build-app", 62 | "build-ios-bundle": "npm run ns-bundle --ios --build-app" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/+task/components/task-list/task-list.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 10 | 11 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | -------------------------------------------------------------------------------- /app/store/effects/task.effects.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Injectable } from '@angular/core'; 3 | import { Action } from '@ngrx/store'; 4 | import { Actions, Effect } from '@ngrx/effects'; 5 | import { Observable } from 'rxjs/Observable'; 6 | import { of } from 'rxjs/observable/of'; 7 | import { TaskState } from '../states/task.state'; 8 | import { TaskService } from '../services/task.service'; 9 | 10 | import { default as taskActions } from '../actions/task.actions'; 11 | import { empty } from 'rxjs/observable/empty'; 12 | 13 | @Injectable() 14 | export class TaskEffects { 15 | 16 | @Effect() create$: Observable = this.actions$ 17 | .ofType(TaskState.ActionTypes.CREATE) 18 | .switchMap(({ payload }) => 19 | this.taskService.create(payload) 20 | .map(() => new taskActions.CreateSuccessAction) 21 | .catch(error => of(new taskActions.CreateFailedAction(error))) 22 | ); 23 | 24 | @Effect() update$: Observable = this.actions$ 25 | .ofType(TaskState.ActionTypes.UPDATE) 26 | .switchMap(({ payload }) => 27 | this.taskService.update(payload) 28 | .map(() => new taskActions.UpdateSuccessAction) 29 | .catch(error => of(new taskActions.UpdateFailedAction(error))) 30 | ); 31 | 32 | @Effect() complete$: Observable = this.actions$ 33 | .ofType(TaskState.ActionTypes.COMPLETE) 34 | .switchMap(({ payload }) => 35 | this.taskService.complete(payload) 36 | .map(() => new taskActions.CompleteSuccessAction) 37 | .catch(error => of(new taskActions.CompleteFailedAction(error))) 38 | ); 39 | 40 | @Effect() delete$: Observable = this.actions$ 41 | .ofType(TaskState.ActionTypes.DELETE) 42 | .switchMap(({ payload }) => 43 | this.taskService.remove(payload) 44 | .map(() => new taskActions.DeleteSuccessAction) 45 | .catch(error => of(new taskActions.DeleteFailedAction(error))) 46 | ); 47 | 48 | @Effect() fetch$: Observable = this.actions$ 49 | .ofType(TaskState.ActionTypes.FETCH) 50 | .switchMap(({ payload }) => 51 | this.taskService.fetch 52 | .map(res => new taskActions.FetchSuccessAction(res)) 53 | .catch(error => of(new taskActions.FetchFailedAction(error))) 54 | ); 55 | 56 | @Effect() clear$: Observable = this.actions$ 57 | .ofType(TaskState.ActionTypes.CLEAR) 58 | .switchMap(() => { 59 | this.taskService.clear(); 60 | return empty(); 61 | }); 62 | 63 | constructor( 64 | private actions$: Actions, 65 | private taskService: TaskService 66 | ) { } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeScript Task App 2 | This repository demonstrates an example task management application built with NativeScript Angular. 3 | 4 | #### Technologies 5 | - NativeScript Angular 6 | - RxJS / NgRx (State / Effects / Actions / Reducers) 7 | - Model-Driven Objects 8 | - Lazy-Loading Modules 9 | - Offline Mode (Persistent Storage / App Settings) 10 | - Angular Reactive Forms 11 | - TypeScript Static Sub-classing for NgRx Actions 12 | 13 | #### Feature List 14 | - Ability to create, update and delete tasks 15 | - Complete task by swiping in from the left. 16 | - Delete tasks by swiping in from the right. 17 | - Update tasks by longpressing on the list item 18 | - Tap "Clear Tasks" to clear all tasks. 19 | - Tap "+" FAB to open modal form for creating tasks. 20 | - All tasks are saved local to the device for persistent storage. 21 | - App branding will change from daytime mode at 8am to nighttime mode at 7pm. 22 | - Filter between complete, incomplete and all tasks by tapping the statistic bar sections. 23 | 24 | ## Getting Started 25 | 1. Download or clone this repository. 26 | 2. Install dependencies `npm i` or `npm install`. 27 | 3. Install and run your desired runtime `tns run ios` or `tns run android`. 28 | 29 | 30 | ### App Images 31 | 32 | |iOS|Android| 33 | |:---:|:---:| 34 | |screen shot 2017-08-26 at 3 37 08 pm|screen shot 2017-08-26 at 8 43 18 pm| 35 | |screen shot 2017-08-26 at 3 36 47 pm|screen shot 2017-08-26 at 8 42 15 pm| 36 | |screen shot 2017-08-26 at 3 34 15 pm|screen shot 2017-08-26 at 8 43 12 pm| 37 | screen shot 2017-08-26 at 3 36 37 pm|screen shot 2017-08-26 at 8 42 50 pm| 38 | 39 | ## Credits / References 40 | * [Zoeyshen](https://dribbble.com/zoeyshen) for the initial graphics. Show her some love on Dribbble. 41 | * [MadebyOliver](https://www.flaticon.com/authors/madebyoliver) for the initial references for the task icons. 42 | 43 | # Contributors 44 | 45 | [Sean perkins](https://github.com/sean-perkins) | 46 | :---: 47 | |[Sean Perkins](https://github.com/sean-perkins)| 48 | -------------------------------------------------------------------------------- /app/store/states/task.state.ts: -------------------------------------------------------------------------------- 1 | import { Task } from './../../models/Task'; 2 | import { IAppState } from './../app.state'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { compose } from '@ngrx/core/compose'; 5 | 6 | export class TaskState { 7 | 8 | static NAMESPACE = 'TaskState'; 9 | 10 | static ActionTypes = { 11 | CLEAR: `${TaskState.NAMESPACE} Clear`, 12 | CREATE: `${TaskState.NAMESPACE} Create Task`, 13 | CREATE_SUCCESS: `${TaskState.NAMESPACE} Create Task Success`, 14 | CREATE_FAILED: `${TaskState.NAMESPACE} Create Task Failed`, 15 | UPDATE: `${TaskState.NAMESPACE} Update Task`, 16 | UPDATE_SUCCESS: `${TaskState.NAMESPACE} Update Task Success`, 17 | UPDATE_FAILED: `${TaskState.NAMESPACE} Update Task Failed`, 18 | DELETE: `${TaskState.NAMESPACE} Delete Task`, 19 | DELETE_SUCCESS: `${TaskState.NAMESPACE} Delete Task Success`, 20 | DELETE_FAILED: `${TaskState.NAMESPACE} Delete Task Failed`, 21 | FETCH: `${TaskState.NAMESPACE} Fetch`, 22 | FETCH_SUCCESS: `${TaskState.NAMESPACE} Fetch Success`, 23 | FETCH_FAILED: `${TaskState.NAMESPACE} Fetch Failed`, 24 | COMPLETE: `${TaskState.NAMESPACE} Complete`, 25 | COMPLETE_SUCCESS: `${TaskState.NAMESPACE} Complete Success`, 26 | COMPleTE_FAILED: `${TaskState.NAMESPACE} Complete Failed` 27 | }; 28 | /** 29 | * The collection of tasks loaded 30 | */ 31 | tasks: Task[]; 32 | /** 33 | * The detail view of a task 34 | */ 35 | task: Task; 36 | /** 37 | * The loading state of the tasks 38 | */ 39 | loading: boolean; 40 | 41 | static state$(state$: Observable): Observable { 42 | return state$.select(state => state.tasks); 43 | } 44 | 45 | static getTasks(state$: Observable) { 46 | return state$.select(state => state.tasks); 47 | } 48 | 49 | static getCompletedTasks(state$: Observable) { 50 | return state$.select(state => state.tasks.filter(res => res.completed)); 51 | } 52 | 53 | static getPendingTasks(state$: Observable) { 54 | return state$.select(state => state.tasks.filter(res => !res.completed)); 55 | } 56 | 57 | static getTaskDetail(state$: Observable) { 58 | return state$.select(state => state.task); 59 | } 60 | 61 | static isTasksLoading(state$: Observable) { 62 | return state$.select(state => state.loading); 63 | } 64 | 65 | constructor(options: TaskState = {}) { 66 | this.tasks = Array.isArray(options.tasks) ? 67 | options.tasks : []; 68 | this.task = options.task || null; 69 | } 70 | 71 | } 72 | 73 | export const getTasks: any = compose(TaskState.getTasks, TaskState.state$); 74 | export const getCompletedTasks: any = compose(TaskState.getCompletedTasks, TaskState.state$); 75 | export const getPendingTasks: any = compose(TaskState.getPendingTasks, TaskState.state$); 76 | export const getTaskDetail: any = compose(TaskState.getTaskDetail, TaskState.state$); 77 | -------------------------------------------------------------------------------- /app/store/actions/task.actions.ts: -------------------------------------------------------------------------------- 1 | import { Task } from './../../models/Task'; 2 | import { TaskState } from './../states/task.state'; 3 | import { Action } from '@ngrx/store'; 4 | 5 | export default class TaskActions { 6 | 7 | // Clear Actions 8 | 9 | static ClearAction = class implements Action { 10 | readonly type = TaskState.ActionTypes.CLEAR; 11 | payload = null; 12 | } 13 | 14 | // Create Actions 15 | 16 | static CreateAction = class implements Action { 17 | readonly type = TaskState.ActionTypes.CREATE; 18 | constructor(public payload: Task) { } 19 | } 20 | 21 | static CreateSuccessAction = class implements Action { 22 | readonly type = TaskState.ActionTypes.CREATE_SUCCESS; 23 | payload = null; 24 | } 25 | 26 | static CreateFailedAction = class implements Action { 27 | readonly type = TaskState.ActionTypes.CREATE_FAILED; 28 | constructor(public payload?: any) { } 29 | } 30 | 31 | // Update Actions 32 | 33 | static UpdateAction = class implements Action { 34 | readonly type = TaskState.ActionTypes.UPDATE; 35 | constructor(public payload: Task) { } 36 | } 37 | 38 | static UpdateSuccessAction = class implements Action { 39 | readonly type = TaskState.ActionTypes.UPDATE_SUCCESS; 40 | payload = null; 41 | } 42 | 43 | static UpdateFailedAction = class implements Action { 44 | readonly type = TaskState.ActionTypes.UPDATE_FAILED; 45 | constructor(public payload?: any) { } 46 | } 47 | 48 | // Delete Actions 49 | 50 | static DeleteAction = class implements Action { 51 | readonly type = TaskState.ActionTypes.DELETE; 52 | constructor(public payload: string) { } 53 | } 54 | 55 | static DeleteSuccessAction = class implements Action { 56 | readonly type = TaskState.ActionTypes.DELETE_SUCCESS; 57 | payload = null; 58 | } 59 | 60 | static DeleteFailedAction = class implements Action { 61 | readonly type = TaskState.ActionTypes.DELETE_FAILED; 62 | constructor(public payload?: any) { } 63 | } 64 | 65 | // Fetch Actions 66 | 67 | static FetchAction = class implements Action { 68 | readonly type = TaskState.ActionTypes.FETCH; 69 | payload = null; 70 | } 71 | 72 | static FetchSuccessAction = class implements Action { 73 | readonly type = TaskState.ActionTypes.FETCH_SUCCESS; 74 | constructor(public payload: any[]) { } 75 | } 76 | 77 | static FetchFailedAction = class implements Action { 78 | readonly type = TaskState.ActionTypes.FETCH_FAILED; 79 | constructor(public payload?: any) { } 80 | } 81 | 82 | // Complete Actions 83 | 84 | static CompleteAction = class implements Action { 85 | readonly type = TaskState.ActionTypes.COMPLETE; 86 | constructor(public payload: Task) { } 87 | } 88 | 89 | static CompleteSuccessAction = class implements Action { 90 | readonly type = TaskState.ActionTypes.COMPLETE_SUCCESS; 91 | payload = null; 92 | } 93 | 94 | static CompleteFailedAction = class implements Action { 95 | readonly type = TaskState.ActionTypes.COMPleTE_FAILED; 96 | constructor(public payload?: any) { } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/+task/components/task-form/task-form.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Page } from 'tns-core-modules/ui/page'; 3 | import { ModalDialogParams } from 'nativescript-angular/modal-dialog'; 4 | import { Color } from 'tns-core-modules/color'; 5 | import { FormGroup, FormBuilder, Validators } from '@angular/forms'; 6 | import { Task } from '../../../models/Task'; 7 | import { NSDate } from '../../../utils/Date'; 8 | /** 9 | * Task Form Component 10 | * Modal page that allows a user to create a new task or update an existing one 11 | */ 12 | @Component({ 13 | moduleId: module.id, 14 | selector: 'ns-task-form', 15 | templateUrl: './task-form.component.html', 16 | styleUrls: ['./task-form.component.css'] 17 | }) 18 | export class TaskFormComponent { 19 | 20 | /** 21 | * The collection of form inputs and validation states 22 | */ 23 | form: FormGroup; 24 | /** 25 | * The submitted state of the form (default: false) 26 | */ 27 | submitted = false; 28 | /** 29 | * Referece to the passed in task (if updating) 30 | */ 31 | task: Task; 32 | /** 33 | * Determines if the form is creating or updating a record 34 | */ 35 | isCreate = true; 36 | 37 | constructor( 38 | private params: ModalDialogParams, 39 | private page: Page, 40 | private fb: FormBuilder) { 41 | this.page.backgroundSpanUnderStatusBar = true; 42 | this.page.backgroundColor = new Color(this.isDayTime ? '#ff9d6e' : '#0061ef'); 43 | 44 | this.initFormGroup(params.context); 45 | 46 | this.page.on('unloaded', () => { 47 | this.params.closeCallback(); 48 | }); 49 | } 50 | 51 | /** 52 | * Initializes the form group and sets existing field values 53 | * @param task The existing task (if updating) 54 | */ 55 | private initFormGroup(task?: any) { 56 | this.task = task.id ? new Task(task) : null; 57 | this.isCreate = !task.id; 58 | this.form = this.fb.group({ 59 | id: [task.id], 60 | name: [task.name, Validators.required], 61 | description: [task.description], 62 | complete: [false] 63 | }); 64 | 65 | if (this.task && this.task.completed) { 66 | this.form.get('complete').setValue(true); 67 | } 68 | } 69 | 70 | /** 71 | * Helper function to get access to the isDayTime in the view 72 | */ 73 | get isDayTime(): boolean { 74 | return NSDate.isDayTime(); 75 | } 76 | 77 | /** 78 | * The cancel action of the dialog - dismisses the modal 79 | */ 80 | cancel(): void { 81 | this.params.closeCallback(); 82 | } 83 | 84 | /** 85 | * The save action of the dialog 86 | * Checks if the form is valid before dismissing 87 | */ 88 | save(value = this.form.value): void { 89 | this.submitted = true; 90 | if (this.form.valid) { 91 | if (value.complete) { 92 | if (!this.task.completedAt) { 93 | value.completedAt = Date.now(); 94 | } 95 | } 96 | else { 97 | value.completedAt = null; 98 | } 99 | this.params.closeCallback(value); 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /app/store/services/task.service.ts: -------------------------------------------------------------------------------- 1 | import { Task } from './../../models/Task'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { Observer } from 'rxjs/Observer'; 5 | import { of } from 'rxjs/observable/of'; 6 | 7 | import * as appSettings from 'tns-core-modules/application-settings'; 8 | 9 | @Injectable() 10 | export class TaskService { 11 | 12 | private static NAMESPACE = 'TASKS'; 13 | 14 | get fetch(): Observable { 15 | return of( 16 | this.tasks 17 | ); 18 | } 19 | 20 | create(task: Task): Observable { 21 | return Observable.create((observer: Observer) => { 22 | if (this.hasTask(task.id)) { 23 | observer.error(`Error: Task already exists with id: ${task.id}.`); 24 | } 25 | else { 26 | this.upsert(task); 27 | observer.next(true); 28 | observer.complete(); 29 | } 30 | return observer; 31 | }); 32 | } 33 | 34 | update(task: Task): Observable { 35 | return Observable.create((observer: Observer) => { 36 | if (this.hasTask(task.id)) { 37 | this.upsert(task); 38 | observer.next(true); 39 | observer.complete(); 40 | } 41 | else { 42 | observer.error(`Error: Could not find task with id: ${task.id}.`); 43 | } 44 | return observer; 45 | }); 46 | } 47 | 48 | remove(taskId: string): Observable { 49 | return Observable.create((observer: Observer) => { 50 | if (this.hasTask(taskId)) { 51 | this.delete(taskId); 52 | observer.next(true); 53 | observer.complete(); 54 | } 55 | else { 56 | observer.error(`Error: Could not find task with id: ${taskId}`); 57 | } 58 | }); 59 | } 60 | 61 | complete(task: Task): Observable { 62 | task.complete(); 63 | return this.update(task); 64 | } 65 | 66 | clear() { 67 | appSettings.remove(TaskService.NAMESPACE); 68 | } 69 | 70 | private indexOfTask(taskId: string): number { 71 | return this.tasks.findIndex(res => res.id === taskId) 72 | } 73 | 74 | private hasTask(id: string): boolean { 75 | return this.tasks 76 | .filter(res => res.id === id).length > 0; 77 | } 78 | 79 | private upsert(task: Task) { 80 | let tasks = this.tasks; 81 | if (this.hasTask(task.id)) { 82 | tasks[this.indexOfTask(task.id)] = task; 83 | } 84 | else { 85 | tasks.push(task); 86 | } 87 | appSettings.setString(TaskService.NAMESPACE, JSON.stringify(tasks)); 88 | } 89 | 90 | private delete(taskId: string) { 91 | let tasks: any[] = this.tasks; 92 | tasks = tasks.filter(res => res.id !== taskId); 93 | appSettings.setString(TaskService.NAMESPACE, JSON.stringify(tasks)); 94 | } 95 | 96 | private get tasks(): any[] { 97 | const items: any = appSettings.getString(TaskService.NAMESPACE); 98 | if (items) { 99 | let tasks = JSON.parse(items); 100 | return tasks 101 | .map(res => new Task(res)); 102 | } 103 | return []; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 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 | -------------------------------------------------------------------------------- /app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "736h", 7 | "filename" : "Default-736h@3x.png", 8 | "minimum-system-version" : "8.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "736h", 16 | "filename" : "Default-Landscape@3x.png", 17 | "minimum-system-version" : "8.0", 18 | "orientation" : "landscape", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "667h", 25 | "filename" : "Default-667h@2x.png", 26 | "minimum-system-version" : "8.0", 27 | "orientation" : "portrait", 28 | "scale" : "2x" 29 | }, 30 | { 31 | "orientation" : "portrait", 32 | "idiom" : "iphone", 33 | "filename" : "Default@2x.png", 34 | "extent" : "full-screen", 35 | "minimum-system-version" : "7.0", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "extent" : "full-screen", 40 | "idiom" : "iphone", 41 | "subtype" : "retina4", 42 | "filename" : "Default-568h@2x.png", 43 | "minimum-system-version" : "7.0", 44 | "orientation" : "portrait", 45 | "scale" : "2x" 46 | }, 47 | { 48 | "orientation" : "portrait", 49 | "idiom" : "ipad", 50 | "filename" : "Default-Portrait.png", 51 | "extent" : "full-screen", 52 | "minimum-system-version" : "7.0", 53 | "scale" : "1x" 54 | }, 55 | { 56 | "orientation" : "landscape", 57 | "idiom" : "ipad", 58 | "filename" : "Default-Landscape.png", 59 | "extent" : "full-screen", 60 | "minimum-system-version" : "7.0", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "orientation" : "portrait", 65 | "idiom" : "ipad", 66 | "filename" : "Default-Portrait@2x.png", 67 | "extent" : "full-screen", 68 | "minimum-system-version" : "7.0", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "orientation" : "landscape", 73 | "idiom" : "ipad", 74 | "filename" : "Default-Landscape@2x.png", 75 | "extent" : "full-screen", 76 | "minimum-system-version" : "7.0", 77 | "scale" : "2x" 78 | }, 79 | { 80 | "orientation" : "portrait", 81 | "idiom" : "iphone", 82 | "filename" : "Default.png", 83 | "extent" : "full-screen", 84 | "scale" : "1x" 85 | }, 86 | { 87 | "orientation" : "portrait", 88 | "idiom" : "iphone", 89 | "filename" : "Default@2x.png", 90 | "extent" : "full-screen", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "orientation" : "portrait", 95 | "idiom" : "iphone", 96 | "filename" : "Default-568h@2x.png", 97 | "extent" : "full-screen", 98 | "subtype" : "retina4", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "orientation" : "portrait", 103 | "idiom" : "ipad", 104 | "extent" : "to-status-bar", 105 | "scale" : "1x" 106 | }, 107 | { 108 | "orientation" : "portrait", 109 | "idiom" : "ipad", 110 | "filename" : "Default-Portrait.png", 111 | "extent" : "full-screen", 112 | "scale" : "1x" 113 | }, 114 | { 115 | "orientation" : "landscape", 116 | "idiom" : "ipad", 117 | "extent" : "to-status-bar", 118 | "scale" : "1x" 119 | }, 120 | { 121 | "orientation" : "landscape", 122 | "idiom" : "ipad", 123 | "filename" : "Default-Landscape.png", 124 | "extent" : "full-screen", 125 | "scale" : "1x" 126 | }, 127 | { 128 | "orientation" : "portrait", 129 | "idiom" : "ipad", 130 | "extent" : "to-status-bar", 131 | "scale" : "2x" 132 | }, 133 | { 134 | "orientation" : "portrait", 135 | "idiom" : "ipad", 136 | "filename" : "Default-Portrait@2x.png", 137 | "extent" : "full-screen", 138 | "scale" : "2x" 139 | }, 140 | { 141 | "orientation" : "landscape", 142 | "idiom" : "ipad", 143 | "extent" : "to-status-bar", 144 | "scale" : "2x" 145 | }, 146 | { 147 | "orientation" : "landscape", 148 | "idiom" : "ipad", 149 | "filename" : "Default-Landscape@2x.png", 150 | "extent" : "full-screen", 151 | "scale" : "2x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const { resolve, join } = require("path"); 2 | 3 | const webpack = require("webpack"); 4 | const nsWebpack = require("nativescript-dev-webpack"); 5 | const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); 6 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 7 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 8 | const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); 9 | 10 | const { AotPlugin } = require("@ngtools/webpack"); 11 | 12 | const mainSheet = `app.css`; 13 | 14 | module.exports = env => { 15 | const platform = getPlatform(env); 16 | 17 | // Default destination inside platforms//... 18 | const path = resolve(nsWebpack.getAppPath(platform)); 19 | 20 | const entry = { 21 | // Discover entry module from package.json 22 | bundle: `./${nsWebpack.getEntryModule()}`, 23 | 24 | // Vendor entry with third-party libraries 25 | vendor: `./vendor`, 26 | 27 | // Entry for stylesheet with global application styles 28 | [mainSheet]: `./${mainSheet}`, 29 | }; 30 | 31 | const rules = getRules(); 32 | const plugins = getPlugins(platform, env); 33 | const extensions = getExtensions(platform); 34 | 35 | const config = { 36 | context: resolve("./app"), 37 | target: nativescriptTarget, 38 | entry, 39 | output: { 40 | pathinfo: true, 41 | path, 42 | libraryTarget: "commonjs2", 43 | filename: "[name].js", 44 | }, 45 | resolve: { 46 | extensions, 47 | 48 | // Resolve {N} system modules from tns-core-modules 49 | modules: [ 50 | "node_modules/tns-core-modules", 51 | "node_modules", 52 | ], 53 | 54 | alias: { 55 | '~': resolve("./app") 56 | }, 57 | }, 58 | node: { 59 | // Disable node shims that conflict with NativeScript 60 | "http": false, 61 | "timers": false, 62 | "setImmediate": false, 63 | "fs": "empty", 64 | }, 65 | module: { rules }, 66 | plugins, 67 | }; 68 | 69 | if (env.snapshot) { 70 | plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ 71 | chunk: "vendor", 72 | projectRoot: __dirname, 73 | webpackConfig: config, 74 | targetArchs: ["arm", "arm64", "ia32"], 75 | tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, 76 | useLibs: false 77 | })); 78 | } 79 | 80 | return config; 81 | }; 82 | 83 | 84 | function getPlatform(env) { 85 | return env.android ? "android" : 86 | env.ios ? "ios" : 87 | () => { throw new Error("You need to provide a target platform!") }; 88 | } 89 | 90 | function getRules() { 91 | return [ 92 | { 93 | test: /\.html$|\.xml$/, 94 | use: [ 95 | "raw-loader", 96 | ] 97 | }, 98 | // Root stylesheet gets extracted with bundled dependencies 99 | { 100 | test: new RegExp(mainSheet), 101 | use: ExtractTextPlugin.extract([ 102 | { 103 | loader: "resolve-url-loader", 104 | options: { silent: true }, 105 | }, 106 | { 107 | loader: "nativescript-css-loader", 108 | options: { minimize: false } 109 | }, 110 | "nativescript-dev-webpack/platform-css-loader", 111 | ]), 112 | }, 113 | // Other CSS files get bundled using the raw loader 114 | { 115 | test: /\.css$/, 116 | exclude: new RegExp(mainSheet), 117 | use: [ 118 | "raw-loader", 119 | ] 120 | }, 121 | // SASS support 122 | { 123 | test: /\.scss$/, 124 | use: [ 125 | "raw-loader", 126 | "resolve-url-loader", 127 | "sass-loader", 128 | ] 129 | }, 130 | 131 | 132 | // Compile TypeScript files with ahead-of-time compiler. 133 | { 134 | test: /\.ts$/, 135 | loaders: [ 136 | "nativescript-dev-webpack/tns-aot-loader", 137 | "@ngtools/webpack", 138 | ] 139 | } 140 | 141 | ]; 142 | } 143 | 144 | function getPlugins(platform, env) { 145 | let plugins = [ 146 | new ExtractTextPlugin(mainSheet), 147 | 148 | // Vendor libs go to the vendor.js chunk 149 | new webpack.optimize.CommonsChunkPlugin({ 150 | name: ["vendor"], 151 | }), 152 | 153 | // Define useful constants like TNS_WEBPACK 154 | new webpack.DefinePlugin({ 155 | "global.TNS_WEBPACK": "true", 156 | }), 157 | 158 | // Copy assets to out dir. Add your own globs as needed. 159 | new CopyWebpackPlugin([ 160 | { from: mainSheet }, 161 | { from: "css/**" }, 162 | { from: "fonts/**" }, 163 | { from: "**/*.jpg" }, 164 | { from: "**/*.png" }, 165 | { from: "**/*.xml" }, 166 | ], { ignore: ["App_Resources/**"] }), 167 | 168 | // Generate a bundle starter script and activate it in package.json 169 | new nsWebpack.GenerateBundleStarterPlugin([ 170 | "./vendor", 171 | "./bundle", 172 | ]), 173 | 174 | // Generate report files for bundles content 175 | new BundleAnalyzerPlugin({ 176 | analyzerMode: "static", 177 | openAnalyzer: false, 178 | generateStatsFile: true, 179 | reportFilename: join(__dirname, "report", `report.html`), 180 | statsFilename: join(__dirname, "report", `stats.json`), 181 | }), 182 | 183 | // Angular AOT compiler 184 | new AotPlugin({ 185 | tsConfigPath: "tsconfig.aot.json", 186 | entryModule: resolve(__dirname, "app/app.module#AppModule"), 187 | typeChecking: false 188 | }), 189 | 190 | // Resolve .ios.css and .android.css component stylesheets, and .ios.html and .android component views 191 | new nsWebpack.UrlResolvePlugin({ 192 | platform: platform, 193 | resolveStylesUrls: true, 194 | resolveTemplateUrl: true 195 | }), 196 | 197 | ]; 198 | 199 | if (env.uglify) { 200 | plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true })); 201 | 202 | // Work around an Android issue by setting compress = false 203 | const compress = platform !== "android"; 204 | plugins.push(new webpack.optimize.UglifyJsPlugin({ 205 | mangle: { except: nsWebpack.uglifyMangleExcludes }, 206 | compress, 207 | })); 208 | } 209 | 210 | return plugins; 211 | } 212 | 213 | // Resolve platform-specific modules like module.android.js 214 | function getExtensions(platform) { 215 | return Object.freeze([ 216 | `.${platform}.ts`, 217 | `.${platform}.js`, 218 | ".aot.ts", 219 | ".ts", 220 | ".js", 221 | ".css", 222 | `.${platform}.css`, 223 | ]); 224 | } 225 | -------------------------------------------------------------------------------- /app/+task/components/task-list/task-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Task } from './../../../models/Task'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { Component, OnInit, ViewContainerRef } from '@angular/core'; 4 | import { Color } from 'tns-core-modules/color'; 5 | import { Store } from '@ngrx/store'; 6 | import { IAppState, getTasks, getCompletedTasks, getPendingTasks, TaskState } from '../../../store/app.state'; 7 | import { Page } from 'tns-core-modules/ui/page'; 8 | import { View, layout } from 'tns-core-modules/ui/core/view'; 9 | import { ListViewEventData, RadListView, SwipeActionsEventData } from 'nativescript-telerik-ui/listview'; 10 | import { Actions } from '@ngrx/effects'; 11 | 12 | import { isAndroid } from 'tns-core-modules/platform'; 13 | 14 | import { NSDate } from '../../../utils/Date'; 15 | 16 | import { default as taskActions } from '../../../store/actions/task.actions'; 17 | import { ModalDialogService, ModalDialogOptions } from 'nativescript-angular/modal-dialog'; 18 | import { TaskFormComponent } from '../task-form/task-form.component'; 19 | 20 | export type SupportedFilters = 'all' | 'completed' | 'pending'; 21 | 22 | @Component({ 23 | moduleId: module.id, 24 | selector: 'ns-task-list', 25 | templateUrl: './task-list.component.html', 26 | styleUrls: ['./task-list.component.css'] 27 | }) 28 | export class TaskListComponent implements OnInit { 29 | // The collection to hold all tasks 30 | tasks$: Observable; 31 | // The collection to hold pending tasks test 32 | completedTasks$: Observable; 33 | // The collection to hold pending (incomplete) tasks 34 | pendingTasks$: Observable; 35 | 36 | leftThresholdPassed = false; 37 | rightThresholdPassed = false; 38 | 39 | private activeFilter: SupportedFilters = 'all'; 40 | 41 | constructor( 42 | private page: Page, 43 | private vcRef: ViewContainerRef, 44 | private modalService: ModalDialogService, 45 | private actions$: Actions, 46 | private store$: Store) { } 47 | 48 | ngOnInit(): void { 49 | this.page.backgroundSpanUnderStatusBar = true; 50 | this.page.actionBarHidden = true; 51 | this.page.backgroundColor = new Color(NSDate.isDayTime() ? '#ff9d6e' : '#0061ef'); 52 | 53 | this.tasks$ = this.store$.let(getTasks); 54 | this.completedTasks$ = this.store$.let(getCompletedTasks); 55 | this.pendingTasks$ = this.store$.let(getPendingTasks); 56 | 57 | this.store$.dispatch(new taskActions.FetchAction); 58 | 59 | this.onTaskDeleteSuccess(); 60 | this.onTaskCompleteSuccess(); 61 | } 62 | 63 | /** 64 | * Gets the collection to be displayed based on the active filter 65 | */ 66 | get displayedTasks$(): Observable { 67 | return this.activeFilter === 'all' ? 68 | this.tasks$ : (this.activeFilter === 'completed' ? this.completedTasks$ : this.pendingTasks$); 69 | } 70 | 71 | /** 72 | * Sets the active filter for displaying segmented results 73 | * @param newFilter The new filter mode 74 | */ 75 | setFilter(newFilter: SupportedFilters = 'all') { 76 | this.activeFilter = newFilter; 77 | } 78 | 79 | /** 80 | * Displays the modal for creating or updating a task 81 | * @param existingTask The task to update (optional) 82 | */ 83 | displayForm(args?: ListViewEventData): void { 84 | this.displayedTasks$.take(1) 85 | .subscribe(tasks => { 86 | const existingTask = args ? tasks[args.index] : undefined; 87 | const options: ModalDialogOptions = { 88 | viewContainerRef: this.vcRef, 89 | context: existingTask, 90 | fullscreen: false, // defaults true on Phone 91 | }; 92 | this.modalService.showModal(TaskFormComponent, options) 93 | .then(formData => { 94 | if (formData) { 95 | const task = new Task((Object).assign({}, existingTask, formData)); 96 | // If the task has an id, update the task 97 | if (task.id !== null) { 98 | console.log('the task', task); 99 | task.update(); 100 | this.store$.dispatch(new taskActions.UpdateAction(task)); 101 | this.onTaskUpdateSuccess(); 102 | } 103 | else { 104 | // otherwise initialize a new task 105 | task.initalize(); 106 | this.store$.dispatch(new taskActions.CreateAction(task)); 107 | this.onTaskCreateSuccess(); 108 | } 109 | } 110 | }); 111 | }); 112 | } 113 | 114 | onLeftSwipeClick(args: ListViewEventData) { 115 | const listView: RadListView = args.object; 116 | listView.notifySwipeToExecuteFinished(); 117 | } 118 | 119 | onRightSwipeClick(args: ListViewEventData) { 120 | const listView = args.object 121 | listView.notifySwipeToExecuteFinished(); 122 | } 123 | 124 | onSwipeCellStarted(args: SwipeActionsEventData) { 125 | var swipeLimits = args.data.swipeLimits; 126 | var swipeView = args.swipeView; 127 | var leftItem = swipeView.getViewById('mark-view'); 128 | var rightItem = swipeView.getViewById('delete-view'); 129 | swipeLimits.left = swipeLimits.right = args.data.x > 0 ? swipeView.getMeasuredWidth() / 2 : swipeView.getMeasuredWidth() / 2; 130 | swipeLimits.threshold = swipeView.getMeasuredWidth(); 131 | } 132 | 133 | onCellSwiping(args: SwipeActionsEventData) { 134 | var swipeLimits = args.data.swipeLimits; 135 | var swipeView = args.swipeView; 136 | var mainView = args.mainView; 137 | var leftItem: View = swipeView.getViewById('mark-view'); 138 | var rightItem: View = swipeView.getViewById('delete-view'); 139 | 140 | if (args.data.x > swipeView.getMeasuredWidth() / 4 && !this.leftThresholdPassed) { 141 | var markLabel = leftItem.getViewById('mark-text'); 142 | this.leftThresholdPassed = true; 143 | } else if (args.data.x < -swipeView.getMeasuredWidth() / 4 && !this.rightThresholdPassed) { 144 | var deleteLabel = rightItem.getViewById('delete-text'); 145 | this.rightThresholdPassed = true; 146 | } 147 | if (args.data.x > 0) { 148 | var leftDimensions = View.measureChild( 149 | leftItem.parent, 150 | leftItem, 151 | layout.makeMeasureSpec(Math.abs(args.data.x), layout.EXACTLY), 152 | layout.makeMeasureSpec(mainView.getMeasuredHeight(), layout.EXACTLY)); 153 | View.layoutChild(leftItem.parent, leftItem, 0, 0, leftDimensions.measuredWidth, leftDimensions.measuredHeight); 154 | } else { 155 | var rightDimensions = View.measureChild( 156 | rightItem.parent, 157 | rightItem, 158 | layout.makeMeasureSpec(Math.abs(args.data.x), layout.EXACTLY), 159 | layout.makeMeasureSpec(mainView.getMeasuredHeight(), layout.EXACTLY)); 160 | 161 | View.layoutChild(rightItem.parent, rightItem, mainView.getMeasuredWidth() - rightDimensions.measuredWidth, 0, mainView.getMeasuredWidth(), rightDimensions.measuredHeight); 162 | } 163 | } 164 | 165 | onSwipeCellFinished(args: SwipeActionsEventData) { 166 | const task: Task = new Task(args.swipeView.bindingContext); 167 | if (this.leftThresholdPassed) { 168 | if (!task.completed) { 169 | this.store$.dispatch(new taskActions.CompleteAction(task)); 170 | } 171 | } else if (this.rightThresholdPassed) { 172 | this.store$.dispatch(new taskActions.DeleteAction(task.id)); 173 | } 174 | this.leftThresholdPassed = false; 175 | this.rightThresholdPassed = false; 176 | } 177 | 178 | /** 179 | * Dispatches an action to clear all existing tasks 180 | * Reloads tasks on completion 181 | */ 182 | clearTask(): void { 183 | this.store$.dispatch(new taskActions.ClearAction); 184 | this.reloadTasks(); 185 | } 186 | 187 | get placeholderImage(): string { 188 | return `res://tasks_complete_${NSDate.isDayTime() ? 'day' : 'night'}`; 189 | } 190 | 191 | private onTaskUpdateSuccess(): void { 192 | this.actions$ 193 | .ofType(TaskState.ActionTypes.UPDATE_SUCCESS) 194 | .take(1) 195 | .do(() => { 196 | this.reloadTasks(); 197 | }) 198 | .subscribe(); 199 | } 200 | 201 | private onTaskCompleteSuccess(): void { 202 | this.actions$ 203 | .ofType(TaskState.ActionTypes.COMPLETE_SUCCESS) 204 | .do(() => { 205 | this.reloadTasks(); 206 | }) 207 | .subscribe(); 208 | } 209 | 210 | private onTaskCreateSuccess(): void { 211 | this.actions$ 212 | .ofType(TaskState.ActionTypes.CREATE_SUCCESS) 213 | .take(1) 214 | .do(() => { 215 | this.reloadTasks(); 216 | }) 217 | .subscribe(); 218 | } 219 | 220 | private onTaskDeleteSuccess(): void { 221 | this.actions$ 222 | .ofType(TaskState.ActionTypes.DELETE_SUCCESS) 223 | .do(() => { 224 | console.log('delete success'); 225 | this.reloadTasks(); 226 | }) 227 | .subscribe(); 228 | } 229 | 230 | private reloadTasks(): void { 231 | this.store$.dispatch(new taskActions.FetchAction); 232 | } 233 | } 234 | --------------------------------------------------------------------------------