├── public └── .npmignore ├── src ├── app │ ├── shared │ │ └── index.ts │ ├── +snaps │ │ ├── shared │ │ │ └── index.ts │ │ ├── +list │ │ │ ├── shared │ │ │ │ └── index.ts │ │ │ ├── list.component.css │ │ │ ├── index.ts │ │ │ ├── list.component.html │ │ │ ├── list.component.spec.ts │ │ │ └── list.component.ts │ │ ├── +post │ │ │ ├── shared │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── post.component.ts │ │ │ ├── post.component.css │ │ │ ├── post.component.spec.ts │ │ │ └── post.component.html │ │ ├── snaps.component.css │ │ ├── snaps.component.html │ │ ├── index.ts │ │ ├── snaps.component.ts │ │ └── snaps.component.spec.ts │ ├── progressive.component.css │ ├── index.ts │ ├── environment.ts │ ├── dribbble.service.spec.ts │ ├── progressive.component.spec.ts │ ├── dribbble.service.ts │ └── progressive.component.ts ├── typings.d.ts ├── favicon.ico ├── icons │ ├── icon.png │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── mstile-70x70.png │ ├── apple-touch-icon.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── android-chrome-36x36.png │ ├── android-chrome-48x48.png │ ├── android-chrome-72x72.png │ ├── android-chrome-96x96.png │ ├── android-chrome-144x144.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-precomposed.png │ ├── ic_menu_24px.svg │ ├── ic_chat_bubble_black_24px.svg │ ├── ic_favorite_black_24px.svg │ ├── ic_visibility_black_24px.svg │ └── safari-pinned-tab.svg ├── system-import.js ├── tsconfig.json ├── main.ts ├── manifest.webapp ├── main-app-shell.ts ├── index.html ├── system-config.ts └── styles │ └── app.css ├── e2e ├── typings.d.ts ├── app.po.ts ├── app.e2e.ts └── tsconfig.json ├── config ├── environment.dev.ts ├── environment.prod.ts ├── environment.js ├── protractor.conf.js ├── karma-test-shim.js └── karma.conf.js ├── .clang-format ├── .editorconfig ├── typings.json ├── .gitignore ├── angular-cli-build.js ├── angular-cli.json ├── README.md ├── tslint.json ├── package.json └── app.yaml /public/.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/+snaps/shared/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/progressive.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/shared/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/shared/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/+snaps/snaps.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/list.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /e2e/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/app/+snaps/snaps.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/index.ts: -------------------------------------------------------------------------------- 1 | export { ListComponent } from './list.component'; 2 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/index.ts: -------------------------------------------------------------------------------- 1 | export { PostComponent } from './post.component'; 2 | -------------------------------------------------------------------------------- /src/app/+snaps/index.ts: -------------------------------------------------------------------------------- 1 | export { SnapsComponent } from './snaps.component'; 2 | -------------------------------------------------------------------------------- /config/environment.dev.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false 3 | }; 4 | -------------------------------------------------------------------------------- /config/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: JavaScript 2 | BasedOnStyle: Google 3 | ColumnLimit: 100 4 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/icon.png -------------------------------------------------------------------------------- /src/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/favicon.ico -------------------------------------------------------------------------------- /src/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/favicon-16x16.png -------------------------------------------------------------------------------- /src/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/favicon-32x32.png -------------------------------------------------------------------------------- /src/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/favicon-96x96.png -------------------------------------------------------------------------------- /src/icons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/mstile-70x70.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /src/icons/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/mstile-144x144.png -------------------------------------------------------------------------------- /src/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/mstile-150x150.png -------------------------------------------------------------------------------- /src/icons/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/mstile-310x150.png -------------------------------------------------------------------------------- /src/icons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/mstile-310x310.png -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | export {environment} from './environment'; 2 | export {ProgressiveAppComponent} from './progressive.component'; 3 | -------------------------------------------------------------------------------- /src/icons/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-36x36.png -------------------------------------------------------------------------------- /src/icons/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-48x48.png -------------------------------------------------------------------------------- /src/icons/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-72x72.png -------------------------------------------------------------------------------- /src/icons/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-96x96.png -------------------------------------------------------------------------------- /src/icons/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-144x144.png -------------------------------------------------------------------------------- /src/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /src/system-import.js: -------------------------------------------------------------------------------- 1 | System.import('system-config.js').then(function () { 2 | System.import('main'); 3 | }).catch(console.error.bind(console)); -------------------------------------------------------------------------------- /src/icons/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/addyosmani/angular2-dribbble-pwa/HEAD/src/icons/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | return { 5 | environment: environment, 6 | baseURL: '/', 7 | locationType: 'auto' 8 | }; 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | export class ProgressivePage { 2 | navigateTo() { 3 | return browser.get('/'); 4 | } 5 | 6 | getParagraphText() { 7 | return element(by.css('progressive-app h1')).getText(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/icons/ic_menu_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/icons/ic_chat_bubble_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/environment.ts: -------------------------------------------------------------------------------- 1 | // The file for the current environment will overwrite this one during build 2 | // Different environments can be found in config/environment.{dev|prod}.ts 3 | // The build system defaults to the dev environment 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /src/icons/ic_favorite_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /e2e/app.e2e.ts: -------------------------------------------------------------------------------- 1 | import { ProgressivePage } from './app.po'; 2 | 3 | describe('progressive App', function() { 4 | let page: ProgressivePage; 5 | 6 | beforeEach(() => { 7 | page = new ProgressivePage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('progressive works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/icons/ic_visibility_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "mapRoot": "", 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "noEmitOnError": true, 11 | "noImplicitAny": false, 12 | "rootDir": ".", 13 | "sourceMap": true, 14 | "sourceRoot": "/", 15 | "target": "es5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ambientDevDependencies": { 3 | "angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459", 4 | "jasmine": "registry:dt/jasmine#2.2.0+20160412134438", 5 | "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654" 6 | }, 7 | "ambientDependencies": { 8 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654", 9 | "node": "registry:dt/node#4.0.0+20160509154515" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/dribbble.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | beforeEachProviders, 3 | it, 4 | describe, 5 | expect, 6 | inject 7 | } from '@angular/core/testing'; 8 | import { DribbbleService } from './dribbble.service'; 9 | 10 | describe('Dribbble Service', () => { 11 | beforeEachProviders(() => [DribbbleService]); 12 | 13 | it('should ...', 14 | inject([DribbbleService], (service: DribbbleService) => { 15 | expect(service).toBeTruthy(); 16 | })); 17 | }); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # IDEs and editors 12 | /.idea 13 | 14 | # misc 15 | /.sass-cache 16 | /connect.lock 17 | /coverage/* 18 | /libpeerconnection.log 19 | npm-debug.log 20 | testem.log 21 | /typings 22 | 23 | # e2e 24 | /e2e/*.js 25 | /e2e/*.map 26 | 27 | #System Files 28 | .DS_Store 29 | Thumbs.db 30 | -------------------------------------------------------------------------------- /src/app/progressive.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | beforeEachProviders, 3 | describe, 4 | expect, 5 | it, 6 | inject 7 | } from '@angular/core/testing'; 8 | import { ProgressiveAppComponent } from '../app/progressive.component'; 9 | 10 | beforeEachProviders(() => [ProgressiveAppComponent]); 11 | 12 | describe('App: Progressive', () => { 13 | it('should create the app', 14 | inject([ProgressiveAppComponent], (app: ProgressiveAppComponent) => { 15 | expect(app).toBeTruthy(); 16 | })); 17 | }); 18 | -------------------------------------------------------------------------------- /angular-cli-build.js: -------------------------------------------------------------------------------- 1 | /* global require, module */ 2 | 3 | var Angular2App = require('angular-cli/lib/broccoli/angular2-app'); 4 | 5 | module.exports = function(defaults) { 6 | return new Angular2App(defaults, { 7 | vendorNpmFiles: [ 8 | 'systemjs/dist/system-polyfills.js', 9 | 'systemjs/dist/system.src.js', 10 | 'zone.js/dist/*.js', 11 | 'es6-shim/es6-shim.js', 12 | 'reflect-metadata/*.js', 13 | 'rxjs/**/*.js', 14 | '@angular/**/*.js', 15 | '@angular2-material/**/*.+(js|css|map|svg)' 16 | ] 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "mapRoot": "", 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "noEmitOnError": true, 11 | "noImplicitAny": false, 12 | "outDir": "../dist/", 13 | "rootDir": ".", 14 | "sourceMap": true, 15 | "target": "es5", 16 | "inlineSources": true 17 | }, 18 | 19 | "files": [ 20 | "main.ts", 21 | "main-app-shell.ts", 22 | "typings.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.2-mobile", 4 | "name": "progressive" 5 | }, 6 | "apps": [ 7 | { 8 | "main": "src/main.ts", 9 | "tsconfig": "src/tsconfig.json", 10 | "mobile": true 11 | } 12 | ], 13 | "addons": [], 14 | "packages": [], 15 | "e2e": { 16 | "protractor": { 17 | "config": "config/protractor.conf.js" 18 | } 19 | }, 20 | "test": { 21 | "karma": { 22 | "config": "config/karma.conf.js" 23 | } 24 | }, 25 | "defaults": { 26 | "prefix": "app", 27 | "sourceDir": "src" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/dribbble.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {JSONP_PROVIDERS, Jsonp} from '@angular/http' 3 | 4 | @Injectable() 5 | export class DribbbleService { 6 | api: string = 'https://api.dribbble.com/v1/'; 7 | token: string = 'bc0239a39745e8604bb996d5ae6cd73ca605d4a0b448de4ab3b21b31fd610966'; 8 | constructor(public jsonp:Jsonp) {} 9 | getPosts(page){ 10 | return this.jsonp.get(this.api + '/shots?page='+ page +'&access_token='+this.token+'&callback=JSONP_CALLBACK'); 11 | } 12 | getPost(id){ 13 | return this.jsonp.get(this.api + '/shots/'+id+'?access_token='+this.token+'&callback=JSONP_CALLBACK'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/post.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { DribbbleService } from '../../dribbble.service'; 3 | import { RouteParams } from '@angular/router-deprecated'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | selector: 'app-post', 8 | templateUrl: 'post.component.html', 9 | styleUrls: ['post.component.css'] 10 | }) 11 | export class PostComponent implements OnInit { 12 | post; 13 | constructor(private dl:DribbbleService, private rp:RouteParams){} 14 | 15 | ngOnInit() { 16 | this.dl.getPost(this.rp.get('id')).subscribe(res => { 17 | this.post = res.json().data; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/+snaps/snaps.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ROUTER_DIRECTIVES, RouteConfig, CanDeactivate, ComponentInstruction } from '@angular/router-deprecated'; 3 | import { PostComponent } from './+post'; 4 | import { DribbbleService } from '../dribbble.service'; 5 | import { ListComponent } from './+list'; 6 | 7 | @Component({ 8 | moduleId: module.id, 9 | selector: 'app-snaps', 10 | templateUrl: 'snaps.component.html', 11 | styleUrls: ['snaps.component.css'], 12 | directives: [ROUTER_DIRECTIVES] 13 | }) 14 | @RouteConfig([ 15 | {path: '/:id', component: PostComponent, name: 'Post'}, 16 | {path: 'list', component: ListComponent, useAsDefault: true} 17 | ]) 18 | export class SnapsComponent {} 19 | 20 | -------------------------------------------------------------------------------- /config/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /*global jasmine */ 2 | var SpecReporter = require('jasmine-spec-reporter'); 3 | 4 | exports.config = { 5 | allScriptsTimeout: 11000, 6 | specs: [ 7 | '../e2e/**/*.e2e.ts' 8 | ], 9 | capabilities: { 10 | 'browserName': 'chrome' 11 | }, 12 | directConnect: true, 13 | baseUrl: 'http://localhost:4200/', 14 | framework: 'jasmine', 15 | jasmineNodeOpts: { 16 | showColors: true, 17 | defaultTimeoutInterval: 30000, 18 | print: function() {} 19 | }, 20 | useAllAngular2AppRoots: true, 21 | beforeLaunch: function() { 22 | require('ts-node').register({ 23 | project: 'e2e' 24 | }); 25 | }, 26 | onPrepare: function() { 27 | jasmine.getEnv().addReporter(new SpecReporter()); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrap } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { ProgressiveAppComponent, environment } from './app/'; 4 | import { APP_SHELL_RUNTIME_PROVIDERS } from '@angular/app-shell'; 5 | 6 | import { provide } from '@angular/core'; 7 | import { Http, HTTP_PROVIDERS, Jsonp, JSONP_PROVIDERS } from '@angular/http' 8 | import { ROUTER_PROVIDERS } from '@angular/router-deprecated' 9 | 10 | import { 11 | PlatformLocation, 12 | Location, 13 | LocationStrategy, 14 | HashLocationStrategy, 15 | PathLocationStrategy, 16 | APP_BASE_HREF} 17 | from '@angular/common'; 18 | 19 | import { DribbbleService } from './app/dribbble.service'; 20 | 21 | if (environment.production) { 22 | enableProdMode(); 23 | } 24 | 25 | bootstrap(ProgressiveAppComponent, [ 26 | APP_SHELL_RUNTIME_PROVIDERS, 27 | JSONP_PROVIDERS, 28 | ROUTER_PROVIDERS, 29 | provide(APP_BASE_HREF, {useValue: '/'}), 30 | DribbbleService]); 31 | -------------------------------------------------------------------------------- /src/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Berry", 3 | "short_name": "Berry", 4 | "icons": [ 5 | { 6 | "src": "icons/android-chrome-36x36.png", 7 | "sizes": "36x36", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "icons/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "icons/android-chrome-72x72.png", 17 | "sizes": "72x72", 18 | "type": "image/png" 19 | }, 20 | { 21 | "src": "icons/android-chrome-96x96.png", 22 | "sizes": "96x96", 23 | "type": "image/png" 24 | }, 25 | { 26 | "src": "icons/android-chrome-144x144.png", 27 | "sizes": "144x144", 28 | "type": "image/png" 29 | }, 30 | { 31 | "src": "icons/android-chrome-192x192.png", 32 | "sizes": "192x192", 33 | "type": "image/png" 34 | }, 35 | { 36 | "src": "icons/android-chrome-512x512.png", 37 | "sizes": "512x512", 38 | "type": "image/png" 39 | } 40 | ], 41 | "theme_color": "#673AB7", 42 | "background_color": "#512DA8", 43 | "start_url": "/index.html", 44 | "display": "standalone", 45 | "orientation": "portrait" 46 | } 47 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/list.component.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/post.component.css: -------------------------------------------------------------------------------- 1 | .post-list-item .post__info--preview { 2 | position: relative; 3 | background: #fff; 4 | z-index: 10; 5 | -webkit-transition: all 100ms ease; 6 | transition: all 100ms ease; 7 | border-radius: 0 0 4px 4px; 8 | top: 0; 9 | } 10 | 11 | .post-preview__details { 12 | position: relative; 13 | min-height: 55px; 14 | } 15 | 16 | .clearfix:before, .clearfix:after { 17 | content: " "; 18 | display: table; 19 | } 20 | 21 | .post-maker { 22 | display: inline-block; 23 | float: left; 24 | margin-right: 10px; 25 | } 26 | 27 | .avatar__wrapper { 28 | position: relative; 29 | display: block; 30 | } 31 | 32 | .avatar__wrapper .avatar { 33 | display: block; 34 | border-radius: 50%; 35 | } 36 | 37 | .post-preview__details a { 38 | color: rgba(0,0,0,0.87); 39 | font-weight: 400; 40 | } 41 | 42 | .post-preview__details h2 { 43 | margin: 0; 44 | font-size: 15px; 45 | line-height: 20px; 46 | padding-right: 5px; 47 | font-weight: 700; 48 | color: rgba(0,0,0,0.87); 49 | } 50 | 51 | .shot_image { 52 | width: 100%; 53 | } 54 | 55 | .shot_description { 56 | padding: 14px; 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular2-dribbble-pwa 2 | 3 | This is a POC of a [Dribbble](https://dribbble.com) API client built using Angular 2 and Angular Mobile Toolkit. [Live demo](https://berry-app.appspot.com). 4 | 5 | Note: Due to the early and changing nature of a lot of the tooling in this space (toolkit, universal and so forth) it is possible that building this app from source will not always work until we lock in our dependency versions. We hope to do so once toolkit and universal are a little more stable, but are sharing this source in case it is useful for learning purposes. 6 | 7 | ### Setup 8 | 9 | Clone or download the repos, then `cd` into the project directory and: 10 | 11 | ``` 12 | $ npm install -g angular-cli && npm install && typings install 13 | ``` 14 | 15 | Once this is done you should be able to `ng serve` to serve up the application or `ng serve --prod` to serve with Service Worker switched on. `ng build --prod` will build a version of the project. You can then serve out of the `dist` directory. 16 | 17 | The repo contains a brief setup using Angular Universal which did work around Google I/O and could be served with `node dist/server.js` once built, but no longer appears to work with more recent versions of the project. We're including a note here in case we get a chance to update the setup to latest. 18 | -------------------------------------------------------------------------------- /src/main-app-shell.ts: -------------------------------------------------------------------------------- 1 | import {provide} from '@angular/core'; 2 | import {APP_BASE_HREF} from '@angular/common'; 3 | import {ProgressiveAppComponent} from './app/'; 4 | import { NODE_LOCATION_PROVIDERS, NODE_ROUTER_PROVIDERS } from 'angular2-universal'; 5 | import { Jsonp, JSONP_PROVIDERS } from '@angular/http'; 6 | 7 | import { 8 | REQUEST_URL, 9 | ORIGIN_URL, 10 | NODE_HTTP_PROVIDERS 11 | } from 'angular2-universal'; 12 | import {APP_SHELL_BUILD_PROVIDERS} from '@angular/app-shell'; 13 | import { DribbbleService } from './app/dribbble.service'; 14 | 15 | export const options = { 16 | directives: [ 17 | // The component that will become the main App Shell 18 | ProgressiveAppComponent 19 | ], 20 | platformProviders: [ 21 | provide(ORIGIN_URL, { 22 | useValue: '' 23 | }), 24 | NODE_LOCATION_PROVIDERS 25 | 26 | ], 27 | providers: [ 28 | APP_SHELL_BUILD_PROVIDERS, 29 | // What URL should Angular be treating the app as if navigating 30 | provide(APP_BASE_HREF, {useValue: '/'}), 31 | provide(REQUEST_URL, {useValue: '/'}), 32 | NODE_ROUTER_PROVIDERS, 33 | NODE_HTTP_PROVIDERS, 34 | JSONP_PROVIDERS, 35 | provide(Jsonp, { 36 | // No-op 37 | useValue: {} 38 | }), 39 | DribbbleService 40 | ], 41 | async: true, 42 | preboot: false 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | beforeEach, 3 | beforeEachProviders, 4 | describe, 5 | expect, 6 | it, 7 | inject, 8 | } from '@angular/core/testing'; 9 | import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; 10 | import { Component } from '@angular/core'; 11 | import { By } from '@angular/platform-browser'; 12 | import { ListComponent } from './list.component'; 13 | 14 | describe('Component: List', () => { 15 | let builder: TestComponentBuilder; 16 | 17 | beforeEachProviders(() => [ListComponent]); 18 | beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) { 19 | builder = tcb; 20 | })); 21 | 22 | it('should inject the component', inject([ListComponent], 23 | (component: ListComponent) => { 24 | expect(component).toBeTruthy(); 25 | })); 26 | 27 | it('should create the component', inject([], () => { 28 | return builder.createAsync(ListComponentTestController) 29 | .then((fixture: ComponentFixture) => { 30 | let query = fixture.debugElement.query(By.directive(ListComponent)); 31 | expect(query).toBeTruthy(); 32 | expect(query.componentInstance).toBeTruthy(); 33 | }); 34 | })); 35 | }); 36 | 37 | @Component({ 38 | selector: 'test', 39 | template: ` 40 | 41 | `, 42 | directives: [ListComponent] 43 | }) 44 | class ListComponentTestController { 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/post.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | beforeEach, 3 | beforeEachProviders, 4 | describe, 5 | expect, 6 | it, 7 | inject, 8 | } from '@angular/core/testing'; 9 | import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; 10 | import { Component } from '@angular/core'; 11 | import { By } from '@angular/platform-browser'; 12 | import { PostComponent } from './post.component'; 13 | 14 | describe('Component: Post', () => { 15 | let builder: TestComponentBuilder; 16 | 17 | beforeEachProviders(() => [PostComponent]); 18 | beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) { 19 | builder = tcb; 20 | })); 21 | 22 | it('should inject the component', inject([PostComponent], 23 | (component: PostComponent) => { 24 | expect(component).toBeTruthy(); 25 | })); 26 | 27 | it('should create the component', inject([], () => { 28 | return builder.createAsync(PostComponentTestController) 29 | .then((fixture: ComponentFixture) => { 30 | let query = fixture.debugElement.query(By.directive(PostComponent)); 31 | expect(query).toBeTruthy(); 32 | expect(query.componentInstance).toBeTruthy(); 33 | }); 34 | })); 35 | }); 36 | 37 | @Component({ 38 | selector: 'test', 39 | template: ` 40 | 41 | `, 42 | directives: [PostComponent] 43 | }) 44 | class PostComponentTestController { 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/app/+snaps/snaps.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | beforeEach, 3 | beforeEachProviders, 4 | describe, 5 | expect, 6 | it, 7 | inject, 8 | } from '@angular/core/testing'; 9 | import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; 10 | import { Component } from '@angular/core'; 11 | import { By } from '@angular/platform-browser'; 12 | import { SnapsComponent } from './snaps.component'; 13 | 14 | describe('Component: Snaps', () => { 15 | let builder: TestComponentBuilder; 16 | 17 | beforeEachProviders(() => [SnapsComponent]); 18 | beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) { 19 | builder = tcb; 20 | })); 21 | 22 | it('should inject the component', inject([SnapsComponent], 23 | (component: SnapsComponent) => { 24 | expect(component).toBeTruthy(); 25 | })); 26 | 27 | it('should create the component', inject([], () => { 28 | return builder.createAsync(SnapsComponentTestController) 29 | .then((fixture: ComponentFixture) => { 30 | let query = fixture.debugElement.query(By.directive(SnapsComponent)); 31 | expect(query).toBeTruthy(); 32 | expect(query.componentInstance).toBeTruthy(); 33 | }); 34 | })); 35 | }); 36 | 37 | @Component({ 38 | selector: 'test', 39 | template: ` 40 | 41 | `, 42 | directives: [SnapsComponent] 43 | }) 44 | class SnapsComponentTestController { 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/app/+snaps/+list/list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; 2 | import { ROUTER_DIRECTIVES, RouteConfig, CanDeactivate, ComponentInstruction } from '@angular/router-deprecated'; 3 | import { DribbbleService } from '../../dribbble.service'; 4 | 5 | import {BehaviorSubject} from 'rxjs/BehaviorSubject'; 6 | import 'rxjs/add/operator/mergeMap'; 7 | import 'rxjs/add/operator/scan'; 8 | 9 | @Component({ 10 | moduleId: module.id, 11 | selector: 'app-list', 12 | templateUrl: 'list.component.html', 13 | styleUrls: ['list.component.css'], 14 | directives: [ROUTER_DIRECTIVES], 15 | // Since using async pipe in template, only run changeDetection 16 | // when a new value is emitted from posts observable 17 | changeDetection: ChangeDetectionStrategy.OnPush 18 | }) 19 | export class ListComponent implements OnInit { 20 | posts; 21 | page = new BehaviorSubject(0).scan((prev:number) => { 22 | // Automatically incremement page whenever next() is called 23 | return prev + 1; 24 | }, 0); 25 | constructor(public dl: DribbbleService) {} 26 | 27 | ngOnInit() { 28 | this.posts = this.page 29 | // Automatically fetch more data when page changes 30 | .mergeMap(page => this.dl.getPosts(page)) 31 | // Merge all pages of data into single list 32 | .scan((acc:any[], res) => acc.concat(res.json().data), []); 33 | } 34 | 35 | routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) { 36 | window.scrollTo(0, 0); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = Infinity; 3 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 4 | 5 | __karma__.loaded = function () { 6 | }; 7 | 8 | var distPath = '/base/dist/'; 9 | var appPath = distPath + 'app/'; 10 | 11 | function isJsFile(path) { 12 | return path.slice(-3) == '.js'; 13 | } 14 | 15 | function isSpecFile(path) { 16 | return path.slice(-8) == '.spec.js'; 17 | } 18 | 19 | function isAppFile(path) { 20 | return isJsFile(path) && (path.substr(0, appPath.length) == appPath); 21 | } 22 | 23 | var allSpecFiles = Object.keys(window.__karma__.files) 24 | .filter(isSpecFile) 25 | .filter(isAppFile); 26 | 27 | // Load our SystemJS configuration. 28 | System.config({ 29 | baseURL: distPath 30 | }); 31 | 32 | System.import('system-config.js').then(function() { 33 | // Load and configure the TestComponentBuilder. 34 | return Promise.all([ 35 | System.import('@angular/core/testing'), 36 | System.import('@angular/platform-browser-dynamic/testing') 37 | ]).then(function (providers) { 38 | var testing = providers[0]; 39 | var testingBrowser = providers[1]; 40 | 41 | testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, 42 | testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS); 43 | }); 44 | }).then(function() { 45 | // Finally, load all spec files. 46 | // This will run the tests directly. 47 | return Promise.all( 48 | allSpecFiles.map(function (moduleName) { 49 | return System.import(moduleName); 50 | })); 51 | }).then(__karma__.start, __karma__.error); -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Progressive 6 | 7 | 8 | {{#unless environment.production}} 9 | 10 | {{/unless}} 11 | 12 | 13 | 14 | 15 | 16 | {{#each mobile.icons}} 17 | 18 | {{/each}} 19 | 20 | 21 | 22 | {{#if environment.production}} 23 | 30 | {{/if}} 31 | 32 | 33 | 34 | Loading... 35 | 36 | {{#if environment.production}} 37 | 38 | {{else}} 39 | {{#each scripts.polyfills}}{{/each}} 40 | 45 | {{/if}} 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | basePath: '..', 4 | frameworks: ['jasmine'], 5 | plugins: [ 6 | require('karma-jasmine'), 7 | require('karma-chrome-launcher') 8 | ], 9 | customLaunchers: { 10 | // chrome setup for travis CI using chromium 11 | Chrome_travis_ci: { 12 | base: 'Chrome', 13 | flags: ['--no-sandbox'] 14 | } 15 | }, 16 | files: [ 17 | { pattern: 'dist/vendor/es6-shim/es6-shim.js', included: true, watched: false }, 18 | { pattern: 'dist/vendor/zone.js/dist/zone.js', included: true, watched: false }, 19 | { pattern: 'dist/vendor/reflect-metadata/Reflect.js', included: true, watched: false }, 20 | { pattern: 'dist/vendor/systemjs/dist/system-polyfills.js', included: true, watched: false }, 21 | { pattern: 'dist/vendor/systemjs/dist/system.src.js', included: true, watched: false }, 22 | { pattern: 'dist/vendor/zone.js/dist/async-test.js', included: true, watched: false }, 23 | 24 | { pattern: 'config/karma-test-shim.js', included: true, watched: true }, 25 | 26 | // Distribution folder. 27 | { pattern: 'dist/**/*', included: false, watched: true } 28 | ], 29 | exclude: [ 30 | // Vendor packages might include spec files. We don't want to use those. 31 | 'dist/vendor/**/*.spec.js' 32 | ], 33 | preprocessors: {}, 34 | reporters: ['progress'], 35 | port: 9876, 36 | colors: true, 37 | logLevel: config.LOG_INFO, 38 | autoWatch: true, 39 | browsers: ['Chrome'], 40 | singleRun: false 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /src/app/progressive.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MdToolbar } from '@angular2-material/toolbar'; 3 | import { MdProgressCircle } from '@angular2-material/progress-circle'; 4 | import { APP_SHELL_DIRECTIVES } from '@angular/app-shell'; 5 | import { SnapsComponent } from './+snaps'; 6 | import { RouteConfig , ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from '@angular/router-deprecated'; 7 | 8 | @Component({ 9 | moduleId: module.id, 10 | selector: 'progressive-app', 11 | styles: [` 12 | md-toolbar[color=primary] { 13 | background: #673AB7; 14 | margin-top: -70px; 15 | will-change: transform; 16 | position: fixed; 17 | z-index: 5; 18 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 19 | } 20 | 21 | md-toolbar a { 22 | color: white; 23 | } 24 | 25 | md-progress-circle[mode="indeterminate"] { 26 | width: 64px; 27 | height: 64px; 28 | margin: 0 8px; 29 | } 30 | 31 | .indicator-container { 32 | height: 0; 33 | margin-top: 50%; 34 | } 35 | .indicator-container md-progress-circle { 36 | margin: -50px auto 0; 37 | } 38 | .indicator-container md-progress-circle[mode="indeterminate"] /deep/ circle { 39 | stroke: #512DA8 !important; 40 | } 41 | 42 | `], 43 | template: ` 44 | 45 | Berry 46 | 47 |
48 | 49 |
50 |
51 | 52 |
53 | `, 54 | directives: [MdToolbar, MdProgressCircle, APP_SHELL_DIRECTIVES, ROUTER_DIRECTIVES] 55 | }) 56 | @RouteConfig([ 57 | {path: '/snaps/...', component: SnapsComponent, as: 'Home', useAsDefault: true} 58 | ]) 59 | export class ProgressiveAppComponent { 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["node_modules/codelyzer"], 3 | "rules": { 4 | "max-line-length": [true, 100], 5 | "no-inferrable-types": true, 6 | "class-name": true, 7 | "comment-format": [ 8 | true, 9 | "check-space" 10 | ], 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "eofline": true, 16 | "no-duplicate-variable": true, 17 | "no-eval": true, 18 | "no-arg": true, 19 | "no-internal-module": true, 20 | "no-trailing-whitespace": true, 21 | "no-bitwise": true, 22 | "no-shadowed-variable": true, 23 | "no-unused-expression": true, 24 | "no-unused-variable": true, 25 | "one-line": [ 26 | true, 27 | "check-catch", 28 | "check-else", 29 | "check-open-brace", 30 | "check-whitespace" 31 | ], 32 | "quotemark": [ 33 | true, 34 | "single", 35 | "avoid-escape" 36 | ], 37 | "semicolon": [true, "always"], 38 | "typedef-whitespace": [ 39 | true, 40 | { 41 | "call-signature": "nospace", 42 | "index-signature": "nospace", 43 | "parameter": "nospace", 44 | "property-declaration": "nospace", 45 | "variable-declaration": "nospace" 46 | } 47 | ], 48 | "curly": true, 49 | "variable-name": [ 50 | true, 51 | "ban-keywords", 52 | "check-format", 53 | "allow-trailing-underscore" 54 | ], 55 | "whitespace": [ 56 | true, 57 | "check-branch", 58 | "check-decl", 59 | "check-operator", 60 | "check-separator", 61 | "check-type" 62 | ], 63 | "component-selector-name": [true, "kebab-case"], 64 | "component-selector-type": [true, "element"], 65 | "host-parameter-decorator": true, 66 | "input-parameter-decorator": true, 67 | "output-parameter-decorator": true, 68 | "attribute-parameter-decorator": true, 69 | "input-property-directive": true, 70 | "output-property-directive": true 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progressive", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "start": "ng server", 8 | "postinstall": "typings install", 9 | "lint": "tslint \"src/**/*.ts\"", 10 | "format": "clang-format -i -style=file --glob=src/**/*.ts", 11 | "test": "ng test", 12 | "pree2e": "webdriver-manager update", 13 | "e2e": "protractor" 14 | }, 15 | "private": true, 16 | "dependencies": { 17 | "@angular/app-shell": "0.0.0", 18 | "@angular/common": "2.0.0-rc.1", 19 | "@angular/compiler": "2.0.0-rc.1", 20 | "@angular/core": "2.0.0-rc.1", 21 | "@angular/http": "2.0.0-rc.1", 22 | "@angular/platform-browser": "2.0.0-rc.1", 23 | "@angular/platform-browser-dynamic": "2.0.0-rc.1", 24 | "@angular/router": "2.0.0-rc.1", 25 | "@angular/router-deprecated": "^2.0.0-rc.1", 26 | "@angular2-material/progress-circle": "^2.0.0-alpha.4", 27 | "@angular2-material/toolbar": "^2.0.0-alpha.4", 28 | "angular2-universal": "^0.100.4", 29 | "es6-shim": "^0.35.0", 30 | "reflect-metadata": "0.1.3", 31 | "rxjs": "5.0.0-beta.6", 32 | "systemjs": "0.19.26", 33 | "vinyl": "^1.1.1", 34 | "zone.js": "^0.6.12" 35 | }, 36 | "devDependencies": { 37 | "@angular/platform-server": "2.0.0-rc.1", 38 | "angular2-broccoli-prerender": "^0.11.0", 39 | "angular2-service-worker": "0.1.11", 40 | "angular2-universal": "^0.100.3", 41 | "angular2-universal-polyfills": "^0.4.1", 42 | "preboot": "^2.0.10", 43 | "angular-cli": "^1.0.0-beta.2-mobile", 44 | "clang-format": "^1.0.35", 45 | "codelyzer": "0.0.14", 46 | "ember-cli-inject-live-reload": "^1.4.0", 47 | "jasmine-core": "^2.4.1", 48 | "jasmine-spec-reporter": "^2.4.0", 49 | "karma": "^0.13.15", 50 | "karma-chrome-launcher": "^0.2.3", 51 | "karma-jasmine": "^0.3.8", 52 | "protractor": "^3.3.0", 53 | "ts-node": "^0.5.5", 54 | "tslint": "^3.6.0", 55 | "typescript": "^1.8.10", 56 | "typings": "^0.8.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/app/+snaps/+post/post.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

{{post.title}}

6 | 20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 |
    32 |
  • 33 | 34 | {{post.likes_count}} 35 | Likes 36 |
  • 37 |
  • 38 | 39 | {{post.views_count}} 40 | Views 41 |
  • 42 |
  • 43 | 44 | {{post.rebounds_count}} 45 | Rebounds 46 |
  • 47 |
  • 48 | 49 | {{post.comments_count}} 50 | Comments 51 |
  • 52 |
  • 53 | 54 | 55 | Link to Dribbble 56 | 57 | 58 |
  • 59 |
60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /src/system-config.ts: -------------------------------------------------------------------------------- 1 | /*********************************************************************************************** 2 | * User Configuration. 3 | **********************************************************************************************/ 4 | /** Map relative paths to URLs. */ 5 | const map: any = {} 6 | 7 | /** User packages configuration. */ 8 | const packages: any = {} 9 | 10 | //////////////////////////////////////////////////////////////////////////////////////////////// 11 | /*********************************************************************************************** 12 | * Everything underneath this line is managed by the CLI. 13 | **********************************************************************************************/ 14 | const barrels: string[] = [ 15 | // Angular specific barrels. 16 | '@angular/app-shell', 17 | '@angular/core', 18 | '@angular/common', 19 | '@angular/compiler', 20 | '@angular/http', 21 | '@angular/router', 22 | '@angular/platform-browser', 23 | '@angular/platform-browser-dynamic', 24 | '@angular2-material/core', 25 | '@angular2-material/toolbar', 26 | '@angular2-material/progress-circle', 27 | '@angular/router-deprecated', 28 | '@angular/upgrade', 29 | '@angular/testing', 30 | // Thirdparty barrels. 31 | 'rxjs', 32 | 33 | // App specific barrels. 34 | 'app', 35 | 'app/shared', 36 | 'app/+snaps', 37 | 'app/+snaps/+post', 38 | 'app/+snaps/+list', 39 | /** @cli-barrel */ 40 | ]; 41 | 42 | const cliSystemConfigPackages: any = {}; 43 | barrels.forEach((barrelName: string) => { 44 | cliSystemConfigPackages[barrelName] = { main: 'index' }; 45 | }); 46 | 47 | /** Type declaration for ambient System. */ 48 | declare var System: any; 49 | 50 | // Apply the CLI SystemJS configuration. 51 | System.config({ 52 | map: { 53 | '@angular': 'vendor/@angular', 54 | '@angular2-material': 'vendor/@angular2-material', 55 | 'rxjs': 'vendor/rxjs', 56 | 'main': 'main.js', 57 | 'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api' 58 | }, 59 | packages: Object.assign({}, cliSystemConfigPackages, { 60 | '@angular2-material/toolbar': { 61 | defaultExtension: 'js', 62 | main: 'toolbar.js' 63 | }, 64 | '@angular2-material/progress-circle': { 65 | defaultExtension: 'js', 66 | main: 'progress-circle.js' 67 | }, 68 | 'angular2-in-memory-web-api': { 69 | defaultExtension: 'js' 70 | } 71 | }) 72 | }); 73 | 74 | // Apply the user's configuration. 75 | System.config({ map, packages }); 76 | -------------------------------------------------------------------------------- /src/styles/app.css: -------------------------------------------------------------------------------- 1 | 2 | html { 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | 7 | .album { 8 | margin-top: 20px; 9 | } 10 | 11 | body { 12 | font-family: Roboto,Helvetica,Arial,sans-serif; 13 | font-size: 14px; 14 | line-height: 1.42857143; 15 | color: #333; 16 | background-color: #FAFAFA; 17 | margin: 0; 18 | padding-top: 70px; 19 | } 20 | 21 | .btn-block { 22 | display: block; 23 | width: 100%; 24 | } 25 | 26 | .container { 27 | padding-right: 15px; 28 | padding-left: 15px; 29 | margin-right: auto; 30 | margin-left: auto; 31 | } 32 | 33 | .container::after { 34 | display: table; 35 | clear: both; 36 | content: ""; 37 | } 38 | 39 | .container-fluid>.navbar-collapse, .container-fluid>.navbar-header, .container>.navbar-collapse, .container>.navbar-header { 40 | margin-right: -15px; 41 | margin-left: -15px; 42 | } 43 | 44 | @media (min-width: 1200px) { 45 | .container { 46 | max-width: 1140px; 47 | } 48 | } 49 | 50 | @media (min-width: 992px) { 51 | .container { 52 | max-width: 940px; 53 | } 54 | } 55 | 56 | a { 57 | color: #337ab7; 58 | text-decoration: none; 59 | } 60 | 61 | .md-thumbnail { 62 | display: block; 63 | padding: 4px; 64 | margin-bottom: 20px; 65 | line-height: 1.42857143; 66 | background-color: #fff; 67 | border: 1px solid #ddd; 68 | /*border-radius: 4px;*/ 69 | -webkit-transition: border .2s ease-in-out; 70 | -o-transition: border .2s ease-in-out; 71 | transition: border .2s ease-in-out; 72 | 73 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); 74 | border-radius: 2px; 75 | } 76 | 77 | .md-thumbnail img { 78 | min-width: 100%; 79 | } 80 | 81 | .md-thumbnail a>img, .md-thumbnail>img { 82 | margin-right: auto; 83 | margin-left: auto; 84 | } 85 | 86 | .md-thumbnail a>img, .md-thumbnail>img { 87 | display: block; 88 | max-width: 100%; 89 | height: auto; 90 | } 91 | 92 | img { 93 | vertical-align: middle; 94 | } 95 | 96 | .md-thumbnail .caption { 97 | padding-left: 8px; 98 | padding-bottom: 24px; 99 | color: #aaa; 100 | font-size: 11px; 101 | padding-top: 15px; 102 | } 103 | 104 | .card { 105 | float: left; 106 | width: 31.333%; 107 | min-width: 190px; 108 | /*padding: .75rem;*/ 109 | margin-right: 10px; 110 | border: 0; 111 | box-shadow: 1px 1px 6px 0 #C6C4C4; 112 | cursor: pointer; 113 | } 114 | 115 | .card img { 116 | max-width: 100%; 117 | } 118 | 119 | .card-text { 120 | font-size: 85%; 121 | } 122 | 123 | @media (max-width: 600px) 124 | { 125 | .card { 126 | width: 90%; 127 | margin: 0 auto 15px; 128 | float: none; 129 | } 130 | } 131 | 132 | .row { 133 | margin-right: -.9375rem; 134 | margin-left: -.9375rem; 135 | } 136 | 137 | .mdl-button { 138 | background: 0 0; 139 | border: none; 140 | border-radius: 2px; 141 | color: #000; 142 | position: relative; 143 | height: 36px; 144 | /*margin: 0;*/ 145 | min-width: 64px; 146 | padding: 0 16px; 147 | /*display: inline-block;*/ 148 | font-family: "Roboto","Helvetica","Arial",sans-serif; 149 | font-size: 14px; 150 | font-weight: 500; 151 | text-transform: uppercase; 152 | letter-spacing: 0; 153 | overflow: hidden; 154 | will-change: box-shadow; 155 | transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1); 156 | outline: none; 157 | cursor: pointer; 158 | text-decoration: none; 159 | text-align: center; 160 | line-height: 36px; 161 | vertical-align: middle; 162 | background: rgba(158,158,158,.2); 163 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); 164 | color: #fff; 165 | background-color: #ff4081; 166 | width: 95%; 167 | margin: 0px 10px 10px 10px; 168 | } 169 | 170 | *, ::after, ::before { 171 | -webkit-box-sizing: inherit; 172 | box-sizing: inherit; 173 | } 174 | 175 | .caption .trophy { 176 | float: left; 177 | width: 54px; 178 | } 179 | 180 | .caption .trophy img { 181 | width: 15px; 182 | max-width: 15px; 183 | min-width: 15px; 184 | margin-bottom: 0px; 185 | } 186 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: berry-app 2 | version: 8 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: true 6 | 7 | default_expiration: "30d" 8 | 9 | handlers: 10 | # web files 11 | - url: /(.*\.(appcache|manifest)) 12 | mime_type: text/cache-manifest 13 | static_files: dist/\1 14 | upload: dist/(.*\.(appcache|manifest)) 15 | expiration: "0m" 16 | 17 | - url: /(.*\.html) 18 | mime_type: text/html 19 | static_files: dist/\1 20 | upload: dist/(.*\.html) 21 | expiration: "1h" 22 | 23 | - url: /(.*\.css) 24 | mime_type: text/css 25 | static_files: dist/\1 26 | upload: dist/(.*\.css) 27 | 28 | - url: /(.*\.js) 29 | mime_type: text/javascript 30 | static_files: dist/\1 31 | upload: dist/(.*\.js) 32 | 33 | - url: /(.*\.json) 34 | mime_type: application/json 35 | static_files: dist/\1 36 | upload: dist/(.*\.json) 37 | expiration: "1h" 38 | 39 | - url: /(.*\.webapp) 40 | mime_type: application/json 41 | static_files: dist/\1 42 | upload: dist/(.*\.webapp) 43 | expiration: "1h" 44 | 45 | - url: /(.*\.xml) 46 | mime_type: application/xml 47 | static_files: dist/\1 48 | upload: dist/(.*\.xml) 49 | expiration: "1h" 50 | 51 | - url: /(.*\.htc) 52 | mime_type: text/x-component 53 | static_files: dist/\1 54 | upload: dist/(.*\.htc) 55 | 56 | - url: /(.*\.txt) 57 | mime_type: text/plain 58 | static_files: dist/\1 59 | upload: dist/(.*\.txt) 60 | 61 | - url: /(.*\.rss) 62 | mime_type: application/rss+xml 63 | static_files: dist/\1 64 | upload: dist/(.*\.rss) 65 | expiration: "1h" 66 | 67 | - url: /(.*\.atom) 68 | mime_type: application/atom+xml 69 | static_files: dist/\1 70 | upload: dist/(.*\.atom) 71 | expiration: "1h" 72 | 73 | - url: /(.*\.crx) 74 | mime_type: application/x-chrome-extension 75 | static_files: dist/\1 76 | upload: dist/(.*\.crx) 77 | 78 | - url: /(.*\.xpi) 79 | mime_type: application/x-xpinstall 80 | static_files: dist/\1 81 | upload: dist/(.*\.xpi) 82 | 83 | - url: /(.*\.safariextz) 84 | mime_type: application/octet-stream 85 | static_files: dist/\1 86 | upload: dist/(.*\.safariextz) 87 | 88 | # font files 89 | - url: /(.*\.eot) 90 | mime_type: application/vnd.ms-fontobject 91 | static_files: dist/\1 92 | upload: dist/(.*\.eot) 93 | 94 | - url: /(.*\.otf) 95 | mime_type: font/opentype 96 | static_files: dist/\1 97 | upload: dist/(.*\.otf) 98 | 99 | - url: /(.*\.ttf) 100 | mime_type: font/truetype 101 | static_files: dist/\1 102 | upload: dist/(.*\.ttf) 103 | 104 | - url: /(.*\.woff) 105 | mime_type: application/x-font-woff 106 | static_files: dist/\1 107 | upload: dist/(.*\.woff) 108 | 109 | # image files 110 | - url: /(.*\.ico) 111 | mime_type: image/x-icon 112 | static_files: dist/\1 113 | upload: dist/(.*\.ico) 114 | expiration: "7d" 115 | 116 | - url: /(.*\.(bmp|gif|ico|jpeg|jpg|png)) 117 | static_files: dist/\1 118 | upload: dist/(.*\.(bmp|gif|ico|jpeg|jpg|png)) 119 | 120 | - url: /(.*\.webp) 121 | mime_type: image/webp 122 | static_files: dist/\1 123 | upload: dist/(.*\.webp) 124 | 125 | - url: /(.*\.(svg|svgz)) 126 | mime_type: image/svg+xml 127 | static_files: dist/\1 128 | upload: dist/(.*\.(svg|svgz)) 129 | 130 | # audio files 131 | - url: /(.*\.(mid|midi|mp3|wav)) 132 | static_files: dist/\1 133 | upload: dist/(.*\.(mid|midi|mp3|wav)) 134 | 135 | - url: /(.*\.(ogg|oga)) 136 | mime_type: audio/ogg 137 | static_files: dist/\1 138 | upload: dist/(.*\.(ogg|oga)) 139 | 140 | # video/media files 141 | - url: /(.*\.m4v) 142 | mime_type: video/m4v 143 | static_files: dist/\1 144 | upload: dist/(.*\.m4v) 145 | 146 | - url: /(.*\.mp4) 147 | mime_type: video/mp4 148 | static_files: dist/\1 149 | upload: dist/(.*\.mp4) 150 | 151 | - url: /(.*\.ogv) 152 | mime_type: video/ogg 153 | static_files: dist/\1 154 | upload: dist/(.*\.ogv) 155 | 156 | - url: /(.*\.webm) 157 | mime_type: video/webm 158 | static_files: dist/\1 159 | upload: dist/(.*\.webm) 160 | 161 | - url: /(.*\.swf) 162 | mime_type: application/x-shockwave-flash 163 | static_files: dist/\1 164 | upload: dist/(.*\.swf) 165 | 166 | - url: /(.*\.unity3d) 167 | mime_type: application/vnd.unity 168 | static_files: dist/\1 169 | upload: dist/(.*\.unity3d) 170 | 171 | # windows files 172 | - url: /(.*\.(doc|exe|ppt|rtf|xls)) 173 | static_files: dist/\1 174 | upload: dist/(.*\.(doc|exe|ppt|rtf|xls)) 175 | 176 | # compressed files 177 | - url: /(.*\.(bz2|gz|rar|tar|tgz|zip)) 178 | static_files: dist/\1 179 | upload: dist/(.*\.(bz2|gz|rar|tar|tgz|zip)) 180 | 181 | # index files 182 | - url: /(.*)/ 183 | static_files: dist/\1/index.html 184 | upload: dist/(.*)/index.html 185 | expiration: "15m" 186 | 187 | # site root 188 | - url: / 189 | static_files: dist/index.html 190 | upload: dist/index.html 191 | expiration: "15m" 192 | 193 | skip_files: 194 | - ^node_modules$ 195 | - node_modules 196 | --------------------------------------------------------------------------------