├── .editorconfig
├── .gitignore
├── README.md
├── angular-cli.json
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.json
├── karma.conf.js
├── npm-debug.log.284780475
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── actions
│ │ ├── feeds.actions.ts
│ │ ├── index.ts
│ │ ├── list.actions.ts
│ │ ├── login.actions.ts
│ │ ├── user-list.actions.ts
│ │ └── user.actions.ts
│ ├── app.module.ts
│ ├── app.routes.ts
│ ├── components
│ │ ├── feed-detail
│ │ │ ├── fd-summary
│ │ │ │ ├── fd-summary.component.css
│ │ │ │ ├── fd-summary.component.html
│ │ │ │ └── fd-summary.component.ts
│ │ │ ├── feed-detail.component.css
│ │ │ ├── feed-detail.component.html
│ │ │ └── feed-detail.component.ts
│ │ ├── feed
│ │ │ ├── feed.component.css
│ │ │ ├── feed.component.html
│ │ │ └── feed.component.ts
│ │ ├── feeds
│ │ │ ├── feeds.component.css
│ │ │ ├── feeds.component.html
│ │ │ └── feeds.component.ts
│ │ ├── index.ts
│ │ ├── shared
│ │ │ └── modal
│ │ │ │ ├── modal.component.css
│ │ │ │ ├── modal.component.html
│ │ │ │ └── modal.component.ts
│ │ └── suggested-lists
│ │ │ ├── suggested-lists.component.css
│ │ │ ├── suggested-lists.component.html
│ │ │ └── suggested-lists.component.ts
│ ├── containers
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.ts
│ │ ├── dashboard-page
│ │ │ ├── dashboard-page.component.css
│ │ │ ├── dashboard-page.component.html
│ │ │ └── dashboard-page.component.ts
│ │ ├── list-feed-page
│ │ │ ├── list-feed-page.component.css
│ │ │ ├── list-feed-page.component.html
│ │ │ └── list-feed-page.component.ts
│ │ ├── login-page
│ │ │ ├── login-page.component.css
│ │ │ ├── login-page.component.html
│ │ │ └── login-page.component.ts
│ │ ├── signup-page
│ │ │ ├── signu-page.component.ts
│ │ │ ├── signup-page.component.css
│ │ │ └── signup-page.component.html
│ │ └── suggestions-page
│ │ │ ├── suggestions-page.component.css
│ │ │ ├── suggestions-page.component.html
│ │ │ └── suggestions-page.component.ts
│ ├── effects
│ │ ├── feeds.effects.ts
│ │ ├── list.effects.ts
│ │ ├── user-auth.effect.ts
│ │ ├── user-lists.effects.ts
│ │ └── user.effects.ts
│ ├── index.ts
│ ├── models
│ │ ├── base.ts
│ │ ├── entities.ts
│ │ ├── index.ts
│ │ ├── list.ts
│ │ ├── tweet.ts
│ │ ├── urls.ts
│ │ ├── user-auth.ts
│ │ ├── user-list.ts
│ │ ├── user-profile.ts
│ │ └── user.ts
│ ├── pipes
│ │ ├── capitalize.ts
│ │ ├── index.ts
│ │ ├── linkify.ts
│ │ ├── mentions.ts
│ │ └── stringify.ts
│ ├── reducers
│ │ ├── feeds.reducer.ts
│ │ ├── index.ts
│ │ ├── list.reducer.ts
│ │ ├── user-auth.reducer.ts
│ │ ├── user-list.reducer.ts
│ │ └── user.reducer.ts
│ ├── services
│ │ ├── api.service.ts
│ │ ├── auth-guard.service.ts
│ │ ├── response-parse.service.ts
│ │ └── user-auth.service.ts
│ └── util.ts
├── assets
│ ├── .gitkeep
│ ├── background.jpg
│ ├── css
│ │ └── style.css
│ └── poi.gif
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.json
└── typings.d.ts
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = 0
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.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 | /.vscode
14 | .project
15 | .classpath
16 | *.launch
17 | .settings/
18 |
19 | # misc
20 | /.sass-cache
21 | /connect.lock
22 | /coverage/*
23 | /libpeerconnection.log
24 | npm-debug.log
25 | testem.log
26 | /typings
27 | secrets.ts
28 | # e2e
29 | /e2e/*.js
30 | /e2e/*.map
31 |
32 | #System Files
33 | .DS_Store
34 | Thumbs.db
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Listify
2 |
3 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.19-3.
4 |
5 | ## Deployed Link
6 |
7 | https://aviabird.github.io/listify/
8 |
9 |
10 | ## Backend Source of Listify
11 |
12 | https://github.com/aviabird/listify-backend
13 |
14 |
15 | ## Backend Deployed Heroku Link
16 |
17 | 'https://listify-backend.herokuapp.com/api'
18 |
19 |
20 | ## Addiional Instructions
21 |
22 | If you are setting up this project for fun on your localhost then
23 | you might want to change the `baseUrl` in `enviornment.ts` to
24 | heroku link: 'https://listify-backend.herokuapp.com/api'
25 |
26 | ## Development server
27 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
28 |
29 | ## Code scaffolding
30 |
31 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class`.
32 |
33 | ## Build
34 |
35 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
36 |
37 | ## Running unit tests
38 |
39 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
40 |
41 | ## Running end-to-end tests
42 |
43 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
44 | Before running the tests make sure you are serving the app via `ng serve`.
45 |
46 | ## Deploying to Github Pages
47 |
48 | Run `ng github-pages:deploy` to deploy to Github Pages.
49 |
50 | ## Further help
51 |
52 | To get more help on the `angular-cli` use `ng --help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
53 |
--------------------------------------------------------------------------------
/angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "project": {
3 | "version": "1.0.0-beta.19-3",
4 | "name": "listify"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "test": "test.ts",
17 | "tsconfig": "tsconfig.json",
18 | "prefix": "ist",
19 | "mobile": false,
20 | "styles": [
21 | "styles.css"
22 | ],
23 | "scripts": [],
24 | "environments": {
25 | "source": "environments/environment.ts",
26 | "dev": "environments/environment.ts",
27 | "prod": "environments/environment.prod.ts"
28 | }
29 | }
30 | ],
31 | "addons": [],
32 | "packages": [],
33 | "e2e": {
34 | "protractor": {
35 | "config": "./protractor.conf.js"
36 | }
37 | },
38 | "test": {
39 | "karma": {
40 | "config": "./karma.conf.js"
41 | }
42 | },
43 | "defaults": {
44 | "styleExt": "css",
45 | "prefixInterfaces": false,
46 | "inline": {
47 | "style": false,
48 | "template": false
49 | },
50 | "spec": {
51 | "class": false,
52 | "component": true,
53 | "directive": true,
54 | "module": false,
55 | "pipe": true,
56 | "service": true
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { IStalkFrontendPage } from './app.po';
2 |
3 | describe('listify App', function() {
4 | let page: IStalkFrontendPage;
5 |
6 | beforeEach(() => {
7 | page = new IStalkFrontendPage();
8 | });
9 |
10 | it('should display message saying app works', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('ist works!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class IStalkFrontendPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('ist-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "outDir": "../dist/out-tsc-e2e",
10 | "sourceMap": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "../node_modules/@types"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', 'angular-cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-remap-istanbul'),
12 | require('angular-cli/plugins/karma')
13 | ],
14 | files: [
15 | { pattern: './src/test.ts', watched: false }
16 | ],
17 | preprocessors: {
18 | './src/test.ts': ['angular-cli']
19 | },
20 | remapIstanbulReporter: {
21 | reports: {
22 | html: 'coverage',
23 | lcovonly: './coverage/coverage.lcov'
24 | }
25 | },
26 | angularCli: {
27 | config: './angular-cli.json',
28 | environment: 'dev'
29 | },
30 | reporters: config.angularCli && config.angularCli.codeCoverage
31 | ? ['progress', 'karma-remap-istanbul']
32 | : ['progress'],
33 | port: 9876,
34 | colors: true,
35 | logLevel: config.LOG_INFO,
36 | autoWatch: true,
37 | browsers: ['Chrome'],
38 | singleRun: false
39 | });
40 | };
41 |
--------------------------------------------------------------------------------
/npm-debug.log.284780475:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/npm-debug.log.284780475
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "listify",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "angular-cli": {},
6 | "scripts": {
7 | "start": "ng serve",
8 | "lint": "tslint \"src/**/*.ts\"",
9 | "test": "ng test",
10 | "pree2e": "webdriver-manager update",
11 | "e2e": "protractor"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/common": "^2.3.1",
16 | "@angular/compiler": "^2.3.1",
17 | "@angular/compiler-cli": "^2.4.1",
18 | "@angular/core": "^2.3.1",
19 | "@angular/forms": "^2.3.1",
20 | "@angular/http": "^2.3.1",
21 | "@angular/platform-browser": "^2.3.1",
22 | "@angular/platform-browser-dynamic": "^2.3.1",
23 | "@angular/platform-server": "^2.4.1",
24 | "@angular/router": "^3.3.1",
25 | "@ngrx/core": "^1.2.0",
26 | "@ngrx/effects": "^2.0.0",
27 | "@ngrx/router-store": "^1.2.5",
28 | "@ngrx/store": "^2.2.1",
29 | "@ngrx/store-devtools": "^3.2.2",
30 | "@ngrx/store-log-monitor": "^3.0.2",
31 | "@types/node": "^6.0.54",
32 | "core-js": "^2.4.1",
33 | "firebase": "^3.6.4",
34 | "karma-remap-istanbul": "^0.2.2",
35 | "ng-semantic": "^1.1.13",
36 | "ng2-restangular": "^0.1.23",
37 | "primeui": "^4.1.15",
38 | "reselect": "^2.5.4",
39 | "rxjs": "^5.0.1",
40 | "ts-helpers": "^1.1.1",
41 | "typescript": "2.0.10",
42 | "zone.js": "^0.7.2"
43 | },
44 | "devDependencies": {
45 | "@types/jasmine": "^2.2.30",
46 | "@types/node": "^6.0.42",
47 | "angular-cli": "1.0.0-beta.19-3",
48 | "codelyzer": "1.0.0-beta.1",
49 | "jasmine-core": "2.4.1",
50 | "jasmine-spec-reporter": "2.5.0",
51 | "karma": "1.2.0",
52 | "karma-chrome-launcher": "^2.0.0",
53 | "karma-cli": "^1.0.1",
54 | "karma-jasmine": "^1.0.2",
55 | "karma-remap-istanbul": "^0.2.1",
56 | "protractor": "4.0.9",
57 | "ts-node": "1.2.1",
58 | "tslint": "3.13.0",
59 | "typescript": "2.0.10",
60 | "webdriver-manager": "10.2.5"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/docs/referenceConf.js
3 |
4 | /*global jasmine */
5 | var SpecReporter = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | './e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function() {}
22 | },
23 | useAllAngular2AppRoots: true,
24 | beforeLaunch: function() {
25 | require('ts-node').register({
26 | project: 'e2e'
27 | });
28 | },
29 | onPrepare: function() {
30 | jasmine.getEnv().addReporter(new SpecReporter());
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/src/app/actions/feeds.actions.ts:
--------------------------------------------------------------------------------
1 | import { Tweet } from './../models/tweet';
2 | import { type } from '../util';
3 | import { Action } from '@ngrx/store';
4 |
5 | // const of all ActionTypes....
6 | export const ActionTypes = {
7 | GET_FEEDS_FOR_ID: type('Get Tweets for Id'),
8 | GET_FEEDS_FOR_ID_SUCCESS: type('Get Tweets for Id Success'),
9 | GET_ALL_FEEDS: type('Get All Feeds'),
10 | GET_ALL_FEEDS_SUCCESS: type('Get All Feeds Success'),
11 | SELECT_FEED: type('Select Feed'),
12 | ADD_FEED_TO_FAV: type('Add Feed To Fav'),
13 | ADD_FEED_TO_FAV_SUCCESS: type('Add Feed To Fav Success'),
14 | REMOVE_FEED_FROM_FAV: type('Remove Feed From Fav'),
15 | REMOVE_FEED_FROM_FAV_SUCESS: type('Remove Feed From Fav Success'),
16 | RETWEET: type('Retweet'),
17 | RETWEET_SUCCESS: type('Retweet Success'),
18 | REPLY: type('Reply'),
19 | REPLY_SUCCESS: type('Reply Success')
20 | }
21 |
22 | export class FeedsActions {
23 |
24 | getAllFeeds(): Action {
25 | return {
26 | type: ActionTypes.GET_ALL_FEEDS
27 | }
28 | }
29 |
30 | /**Changed Feeds type to any from Tweet
31 | * TODO: make a complete model of tweet
32 | */
33 | getAllFeedsSuccess(feeds: any): Action {
34 | return {
35 | type: ActionTypes.GET_ALL_FEEDS_SUCCESS,
36 | payload: feeds
37 | }
38 | }
39 |
40 | getFeedsForId(userListId: any): Action {
41 | return {
42 | type: ActionTypes.GET_FEEDS_FOR_ID,
43 | payload: userListId
44 | }
45 | }
46 |
47 | /**
48 | * Change Feeds type to any from Tweet
49 | *
50 | * TODO: make a complete model of tweet - voidzero
51 | *
52 | * Get Feeds For Paticular List ID
53 | *
54 | * @param : feeds
55 | *
56 | * @return : Action With payload feeds
57 | */
58 | getFeedsForIdSuccess(feeds: any): Action {
59 | return {
60 | type: ActionTypes.GET_FEEDS_FOR_ID_SUCCESS,
61 | payload: feeds
62 | }
63 | }
64 |
65 | /**
66 | * Action to selcet a feed and add it to store
67 | *
68 | * @param: feedId
69 | *
70 | * @return Action with payload feedId
71 | */
72 | selectFeed(feedId: string): Action {
73 | return {
74 | type: ActionTypes.SELECT_FEED,
75 | payload: feedId
76 | }
77 | }
78 |
79 | /**
80 | * Action that triggers when user adds
81 | * a particular feed to Fav
82 | *
83 | * @param : feed
84 | *
85 | * @return : Action with payload Feed
86 | */
87 | addFeedToFav(feed: any):Action{
88 | return {
89 | type: ActionTypes.ADD_FEED_TO_FAV,
90 | payload: feed
91 | }
92 | }
93 | /**
94 | * Action that triggers when fav action completes
95 | *
96 | * @param : feed
97 | *
98 | * @return : Action with payload Feed
99 | */
100 | addFeedToFavSuccess(feed: any): Action {
101 | return {
102 | type: ActionTypes.ADD_FEED_TO_FAV_SUCCESS,
103 | payload: feed
104 | }
105 | }
106 |
107 | /**
108 | * Action that triggers when user removes
109 | * a particular feed to Fav
110 | *
111 | * @param : feed
112 | *
113 | * @return : Action with payload Feed
114 | */
115 | removeFeedFromFav(feed: any):Action{
116 | return {
117 | type: ActionTypes.REMOVE_FEED_FROM_FAV,
118 | payload: feed
119 | }
120 | }
121 |
122 | /**
123 | * Action that triggers when remove fav action completes
124 | *
125 | * @param : feed
126 | *
127 | * @return : Action with payload Feed
128 | */
129 | removeFeedFromFavSuccess(feed: any): Action {
130 | return {
131 | type: ActionTypes.REMOVE_FEED_FROM_FAV_SUCESS,
132 | payload: feed
133 | }
134 | }
135 |
136 | /**
137 | * Action that triggers when retweet is clicked
138 | *
139 | * @param : feed
140 | *
141 | * @return : Action with payload Feed
142 | */
143 | retweet(feed): Action {
144 | return {
145 | type: ActionTypes.RETWEET,
146 | payload: feed
147 | }
148 | }
149 |
150 | /**
151 | * Action that triggers when retweet is Success
152 | *
153 | * @param : feed
154 | *
155 | * @return : Action with payload feed
156 | */
157 | retweetSuccess(feed): Action {
158 | return {
159 | type: ActionTypes.RETWEET_SUCCESS,
160 | payload: feed
161 | }
162 | }
163 |
164 | /**
165 | * Action that triggers when a user replies to
166 | * a particular tweet
167 | *
168 | * @param : {feedId: feedId, message: message}
169 | *
170 | * @return : Action with payload messageWithFeed
171 | */
172 | reply(messageWithFeed: {}): Action {
173 | return {
174 | type: ActionTypes.REPLY,
175 | payload: messageWithFeed
176 | }
177 | }
178 |
179 | /**
180 | * Action that triggers when a reply to
181 | * a tweet is success
182 | *
183 | * @param : {boolean} status
184 | *
185 | * @return : Action with payload status
186 | */
187 | replySuccess(feed): Action {
188 | return {
189 | type: ActionTypes.REPLY_SUCCESS,
190 | payload: feed
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/app/actions/index.ts:
--------------------------------------------------------------------------------
1 | import { LoginActions } from './login.actions';
2 | import { UserActions } from './user.actions';
3 | import { ListActions } from './list.actions';
4 | import { FeedsActions } from './feeds.actions';
5 | import { UserListActions } from './user-list.actions';
6 | export {
7 | LoginActions,
8 | UserActions,
9 | ListActions,
10 | FeedsActions,
11 | UserListActions
12 | };
13 |
14 | export default [
15 | LoginActions,
16 | UserActions,
17 | ListActions,
18 | FeedsActions,
19 | UserListActions
20 | ];
--------------------------------------------------------------------------------
/src/app/actions/list.actions.ts:
--------------------------------------------------------------------------------
1 | import { type } from '../util';
2 | import { Action } from '@ngrx/store';
3 | import { List } from '../models';
4 | import { UserList } from '../models';
5 |
6 | export const ActionTypes = {
7 | RETRIVE_LISTS: type('Retrive Lists'),
8 | RETRIVE_LISTS_SUCCESS: type('Retrive Lists Success'),
9 | FOLLOW_LIST: type('Follow List'),
10 | FOLLOW_LIST_SUCCESS: type('Follow List Success'),
11 | UNFOLLOW_LIST: type('UnFollow List'),
12 | UNFOLLOW_LIST_SUCCESS: type('UnFollow List Success'),
13 | UPDATE_LISTS: type('Update Lists')
14 | }
15 |
16 | export class ListActions {
17 |
18 | retriveLists(): Action {
19 | return {
20 | type: ActionTypes.RETRIVE_LISTS
21 | }
22 | }
23 |
24 | retriveListsSuccess(suggestedLists: List[]): Action {
25 | return {
26 | type: ActionTypes.RETRIVE_LISTS_SUCCESS,
27 | payload: suggestedLists
28 | }
29 | }
30 |
31 | follow(listId: string): Action {
32 | return {
33 | type: ActionTypes.FOLLOW_LIST,
34 | payload: listId
35 | }
36 | }
37 |
38 | followSuccess(response: any): Action {
39 | return {
40 | type: ActionTypes.FOLLOW_LIST_SUCCESS,
41 | payload: response
42 | }
43 | }
44 |
45 | unFollowList(listId: string): Action{
46 | return {
47 | type: ActionTypes.UNFOLLOW_LIST,
48 | payload: listId
49 | }
50 | }
51 |
52 | unFollowListSuccess(response: any): Action {
53 | return {
54 | type: ActionTypes.UNFOLLOW_LIST_SUCCESS,
55 | payload: response
56 | }
57 | }
58 |
59 | updateLists(response: any): Action {
60 | return {
61 | type: ActionTypes.UPDATE_LISTS,
62 | payload: response
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/app/actions/login.actions.ts:
--------------------------------------------------------------------------------
1 | import { type } from '../util';
2 | import { Action } from '@ngrx/store';
3 | import { UserAuth } from '../models';
4 | /**
5 | * A Hash Constant which has all types of Login Action
6 | * { key: type }
7 | */
8 | export const ActionTypes = {
9 | LOGIN: type("Login"),
10 | LOGIN_SUCCESS: type("Login Success"),
11 | LOGIN_SERVER: type("Login Server"),
12 | LOGIN_SERVER_SUCCESS: type("Login Server Success"),
13 | LOGOUT: type('Logout'),
14 | LOGOUT_SUCCESS: type('Logout Success'),
15 | SIGNUP: type('Sign Up'),
16 | SIGNUP_SUCCESS: type('Sign Up Success'),
17 | SIGNUP_SERVER: type('Sign Up Server'),
18 | SIGNUP_SERVER_SUCCESS: type('Sign Up Server Success'),
19 | STORE_USER: type('Store User'),
20 | STORE_USER_SUCCESS: type('Store User Success')
21 | }
22 |
23 | /**
24 | * List of all functions i.e action that can be
25 | * performed while logging in a user;
26 | */
27 | export class LoginActions {
28 | /**
29 | * Returns an action.
30 | *
31 | * @param provider: a string to specify the social media from which the user is logging in. E.g: 'facebook'
32 | * @return {Action} an Action with type 'Login' and a payload provider
33 | */
34 | login(provider: string): Action {
35 | return {
36 | type: ActionTypes.LOGIN,
37 | payload: provider
38 | };
39 | }
40 |
41 | /**
42 | * Returns an Action.
43 | *
44 | * Note : taking user_auth as input and returning user as a payload
45 | * which latter a reducer stores it in a state.
46 | * There is an another approach where we take a loginResponse
47 | * as whole and then in reducer we disect it and retrive user
48 | * details.
49 | *
50 | * TODO: Discuss the above two approach with the team.
51 | * and implement the elegant one.
52 | *
53 | * Note: Ignore the above message, currently takking login response
54 | * as payload.
55 | *
56 | * @param loginData: Data of type any received after login a user
57 | * @return {Action} an Action with type 'Login Success'
58 | *
59 | */
60 | loginSuccess(userAuth: UserAuth): Action {
61 | return {
62 | type: ActionTypes.LOGIN_SUCCESS,
63 | payload: userAuth
64 | }
65 | }
66 |
67 | /**
68 | * Returns an Action
69 | *
70 | * @return {Action} an Action with type 'Logout'
71 | */
72 | logout(): Action {
73 | return {
74 | type: ActionTypes.LOGOUT
75 | }
76 | }
77 |
78 | /**
79 | * Returns an Action
80 | *
81 | * @return {Action} an Action with type 'Logout Success'
82 | */
83 | logoutSuccess(userAuth): Action {
84 | return {
85 | type: ActionTypes.LOGOUT_SUCCESS
86 | }
87 | }
88 |
89 | signUp(provider: string): Action {
90 | return {
91 | type: ActionTypes.SIGNUP,
92 | payload: provider
93 | }
94 | }
95 |
96 | signUpSuccess(userAuth: UserAuth): Action {
97 | return {
98 | type: ActionTypes.SIGNUP_SUCCESS,
99 | payload: userAuth
100 | }
101 | }
102 |
103 |
104 | storeUser(email, userAuth): Action {
105 | return {
106 | type: ActionTypes.STORE_USER,
107 | payload: { email: email, userAuth: userAuth }
108 | }
109 | }
110 |
111 | storeUserSuccess(userAuth): Action {
112 | return {
113 | type: ActionTypes.STORE_USER_SUCCESS,
114 | payload: userAuth
115 | }
116 | }
117 |
118 | loginServer(userAuth): Action {
119 | return {
120 | type: ActionTypes.LOGIN_SERVER,
121 | payload: userAuth
122 | }
123 | }
124 |
125 | loginServerSuccess(userAuth): Action {
126 | return {
127 | type: ActionTypes.LOGIN_SERVER_SUCCESS,
128 | payload: userAuth
129 | }
130 | }
131 |
132 | signUpServer(userAuth): Action {
133 | return {
134 | type: ActionTypes.SIGNUP_SERVER,
135 | payload: userAuth
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/app/actions/user-list.actions.ts:
--------------------------------------------------------------------------------
1 | import { type } from '../util';
2 | import { Action } from '@ngrx/store';
3 | import { UserList } from '../models/';
4 |
5 | export const ActionTypes = {
6 | GET_USER_LISTS: type("Get User List"),
7 | GET_USER_LISTS_SUCCESS: type("Get User List Success")
8 | }
9 |
10 |
11 | export class UserListActions {
12 | getUserLists(): Action {
13 | return {
14 | type: ActionTypes.GET_USER_LISTS
15 | }
16 | }
17 |
18 | getUserListsSuccess(userLists: UserList[]): Action {
19 | return {
20 | type: ActionTypes.GET_USER_LISTS_SUCCESS,
21 | payload: userLists
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/app/actions/user.actions.ts:
--------------------------------------------------------------------------------
1 | import { type } from '../util';
2 | import { Action } from '@ngrx/store';
3 | import { User } from '../models/';
4 |
5 | /**
6 | * A Hash Constant which has all types of Login Action
7 | * { key: type }
8 | */
9 | export const ActionTypes = {
10 | LOAD_PROFILE: type("Load Profile"),
11 | LOAD_PROFILE_SUCCESS: type("Load Profile Success")
12 | }
13 |
14 | /**
15 | * List of all Action.
16 | * TODO: Naming Convention for this Action class
17 | * Hint: These are Actions performed on a logged in user to gets its details
18 | * or are the actions that are performed by dashboard.
19 | */
20 | export class UserActions {
21 | /**
22 | * Returns an Action
23 | *
24 | * @return {Action} an Action with type 'Load Profile' and null payload.
25 | */
26 | loadProfile(): Action {
27 | return {
28 | type: ActionTypes.LOAD_PROFILE
29 | }
30 | }
31 |
32 | /**
33 | * Returns an Action
34 | *
35 | * @return {Action} an Action with type 'Load Profile Success'
36 | * and payload as User
37 | */
38 | loadProfileSuccess(user): Action {
39 | return {
40 | type: ActionTypes.LOAD_PROFILE_SUCCESS,
41 | payload: user
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | //========================= IStalk ========================================
2 | /**
3 | * Core Modules and Libraries used in App.
4 | */
5 | import { BrowserModule } from '@angular/platform-browser';
6 | import { NgModule } from '@angular/core';
7 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
8 | import { HttpModule } from '@angular/http';
9 | import { CommonModule } from '@angular/common';
10 | import { RestangularModule } from 'ng2-restangular';
11 | import { environment } from '../environments/environment';
12 | import { PipesModule } from './pipes';
13 |
14 | /**Semantic UI */
15 | import { NgSemanticModule } from 'ng-semantic';
16 |
17 | /** ALL Services used in APP */
18 | import { AuthGuardService } from './services/auth-guard.service';
19 | import { UserAuthService } from './services/user-auth.service';
20 | import { ApiService } from './services/api.service';
21 | import { ResponseParseService } from './services/response-parse.service';
22 |
23 | //========================= NGRX Releated Imports ===========================
24 |
25 | /**
26 | * Ngrx Store Modules
27 | */
28 | import { StoreModule } from '@ngrx/store';
29 | import { StoreDevtoolsModule } from '@ngrx/store-devtools';
30 | import { StoreLogMonitorModule, useLogMonitor } from '@ngrx/store-log-monitor';
31 | import { EffectsModule } from '@ngrx/effects';
32 | import { RouterStoreModule } from '@ngrx/router-store';
33 |
34 | /** All SideEffects in APP */
35 | import { UserAuthEffects } from './effects/user-auth.effect';
36 | import { UserEffects } from './effects/user.effects';
37 | import { ListEffects } from './effects/list.effects';
38 | import { FeedsEffects } from './effects/feeds.effects';
39 | import { UserListsEffects } from './effects/user-lists.effects';
40 |
41 | /**ALL Ngrx Actions that can be fired in app loaded as one.*/
42 | import actions from './actions';
43 |
44 | /**Global Reducer of APP */
45 | import reducer from './reducers';
46 |
47 | //======================================================================
48 |
49 | /**All Components in APP */
50 | import { ComponentsModule } from './components';
51 |
52 | /**All Routes in APP */
53 | import { routing } from './app.routes';
54 |
55 | /**All Containers in APP */
56 | import { SignUpPageComponent } from './containers/signup-page/signu-page.component';
57 | import { ListFeedPageComponent } from './containers/list-feed-page/list-feed-page.component';
58 | import { SuggestionsPageComponent } from './containers/suggestions-page/suggestions-page.component';
59 | import { DashboardPageComponent } from './containers/dashboard-page/dashboard-page.component';
60 | import { LoginPageComponent } from './containers/login-page/login-page.component';
61 | import { AppComponent } from './containers/app.component';
62 |
63 | @NgModule({
64 | declarations: [
65 | AppComponent,
66 | LoginPageComponent,
67 | SignUpPageComponent,
68 | DashboardPageComponent,
69 | SuggestionsPageComponent,
70 | ListFeedPageComponent
71 | ],
72 | imports: [
73 | ComponentsModule,
74 | NgSemanticModule,
75 | BrowserModule,
76 | FormsModule,
77 | ReactiveFormsModule,
78 | HttpModule,
79 | ComponentsModule,
80 | PipesModule,
81 | CommonModule,
82 | routing,
83 | // Importing RestangularModule and making default configs for restanglar
84 | RestangularModule.forRoot((RestangularProvider) => {
85 | RestangularProvider.setBaseUrl(environment.baseUrl);
86 | RestangularProvider.setDefaultHeaders({'Content-Type':'application/json'});
87 | }
88 | ),
89 | StoreModule.provideStore(reducer),
90 | RouterStoreModule.connectRouter(),
91 | StoreDevtoolsModule.instrumentStore({
92 | monitor: useLogMonitor({
93 | visible: false,
94 | position: 'right'
95 | })
96 | }),
97 | StoreLogMonitorModule,
98 | EffectsModule.run(UserAuthEffects),
99 | EffectsModule.run(UserEffects),
100 | EffectsModule.run(ListEffects),
101 | EffectsModule.run(FeedsEffects),
102 | EffectsModule.run(UserListsEffects)
103 | ],
104 | providers: [
105 | actions,
106 | UserAuthService,
107 | AuthGuardService,
108 | ApiService,
109 | ResponseParseService
110 | ],
111 | bootstrap: [AppComponent]
112 | })
113 | export class AppModule { }
114 |
--------------------------------------------------------------------------------
/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | /**Required Angular 2 Modules for Router */
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { AuthGuardService } from './services/auth-guard.service';
4 |
5 | /** Componets required for routing */
6 | import { SuggestionsPageComponent } from './containers/suggestions-page/suggestions-page.component';
7 | import { ListFeedPageComponent } from './containers/list-feed-page/list-feed-page.component';
8 | import { FeedDetailComponent } from './components/feed-detail/feed-detail.component';
9 | import { FeedsComponent } from './components/feeds/feeds.component';
10 | import { DashboardPageComponent } from './containers/dashboard-page/dashboard-page.component';
11 | import { SignUpPageComponent } from './containers/signup-page/signu-page.component';
12 | import { LoginPageComponent } from './containers/login-page/login-page.component';
13 |
14 | const routes: Routes = [
15 | { path: '', redirectTo: 'login', pathMatch: 'full'},
16 | { path: 'login', component: LoginPageComponent },
17 | { path: 'request-email', component: SignUpPageComponent },
18 | { path: 'dashboard', component: DashboardPageComponent,
19 | children: [
20 | { path: '', redirectTo: 'feeds', pathMatch: 'full' },
21 | { path: 'feeds',
22 | component: FeedsComponent,
23 | children: [
24 | { path: 'tweet/:tweet_id', component: FeedDetailComponent }
25 | ]
26 | },
27 | {
28 | path: 'feeds/:id',
29 | component: ListFeedPageComponent,
30 | children: [
31 | { path: 'tweet/:tweet_id', component: FeedDetailComponent }
32 | ]
33 | },
34 | { path: 'suggestions',
35 | component: SuggestionsPageComponent
36 | },
37 | ],
38 | canLoad: [AuthGuardService]
39 | }
40 | ];
41 |
42 | export const routing = RouterModule.forRoot(routes);
--------------------------------------------------------------------------------
/src/app/components/feed-detail/fd-summary/fd-summary.component.css:
--------------------------------------------------------------------------------
1 | .center-pad {
2 | padding: 25px;
3 | }
4 |
5 | .rounded-border{
6 | border-radius: 5px;
7 | }
8 |
9 | .full-width-hr{
10 | width: 100%;
11 | }
--------------------------------------------------------------------------------
/src/app/components/feed-detail/fd-summary/fd-summary.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
}})
6 |
7 |
8 |
13 |
14 | @{{feed.user.screen_name}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Created at: {{feed.created_at | date:'short'}}
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
39 | {{ feed.favorite_count }}
40 |
41 |
42 |
43 |
44 |
45 |
54 |
55 |
56 |
57 | {{ reply.text }}
58 |
59 |
60 |
61 |
62 |
63 | Show this project some ❤ on
Github
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/app/components/feed-detail/fd-summary/fd-summary.component.ts:
--------------------------------------------------------------------------------
1 | import { FeedsActions } from './../../../actions/feeds.actions';
2 | import { ApiService } from './../../../services/api.service';
3 | import { Component, Input, Output, EventEmitter } from '@angular/core';
4 | @Component({
5 | selector: 'ist-fd-summary',
6 | templateUrl: './fd-summary.component.html',
7 | styleUrls: ['./fd-summary.component.css']
8 | })
9 | export class FdSummaryComponent {
10 | @Input() feed: any;
11 | @Output() replyClicked = new EventEmitter();
12 | mentions;
13 | constructor(private api: ApiService){
14 | }
15 |
16 | reply(message: any){
17 | var payload = { feed: this.feed, message: message}
18 | this.replyClicked.emit(payload);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/components/feed-detail/feed-detail.component.css:
--------------------------------------------------------------------------------
1 | .center-align{
2 | text-align: center;
3 | }
--------------------------------------------------------------------------------
/src/app/components/feed-detail/feed-detail.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/app/components/feed-detail/feed-detail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
2 | import { Router, ActivatedRoute } from '@angular/router';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Store } from '@ngrx/store';
5 | import { AppState, getSelectedFeed } from '../../reducers/index';
6 | import { Subscription } from 'rxjs';
7 | import { ApiService } from '../../services/api.service';
8 | import { FeedsActions } from '../../actions/feeds.actions';
9 | import { back } from '@ngrx/router-store';
10 |
11 | declare var $: any;
12 |
13 | @Component({
14 | selector: 'ist-feed-detail',
15 | templateUrl: './feed-detail.component.html',
16 | styleUrls: ['./feed-detail.component.css'],
17 | changeDetection: ChangeDetectionStrategy.OnPush
18 | })
19 | export class FeedDetailComponent implements OnInit, OnDestroy {
20 | private subscription: Subscription;
21 | feed: Observable;
22 | feedId: any;
23 | constructor(public store: Store,
24 | private router: Router,
25 | private route: ActivatedRoute,
26 | private feedActions: FeedsActions) {
27 | this.feed = this.store.select(getSelectedFeed);
28 | }
29 |
30 | ngOnInit() {
31 | this.subscription = this.route.params.subscribe(
32 | (params: any) => {
33 | this.feedId = params['tweet_id'];
34 | this.store.dispatch(this.feedActions.selectFeed(this.feedId));
35 | }
36 | );
37 | this.loadModal();
38 | }
39 |
40 | reply(messageWithFeed: any){
41 | this.store.dispatch(this.feedActions.reply(messageWithFeed));
42 | }
43 |
44 | loadModal(){
45 | let that = this;
46 | try{
47 | $('.ui.modal').modal({
48 | onApprove : function() { return false; },
49 | onHide: function(){
50 | that.store.dispatch(back());
51 | $('.ui.modal').remove();
52 | }
53 | }).modal('show');
54 | } catch(e) {
55 |
56 | console.log("Error is",e);
57 | }
58 | }
59 |
60 | ngOnDestroy(){
61 | this.subscription.unsubscribe();
62 | }
63 | }
--------------------------------------------------------------------------------
/src/app/components/feed/feed.component.css:
--------------------------------------------------------------------------------
1 | .clickable:hover{
2 | cursor: pointer;
3 | }
--------------------------------------------------------------------------------
/src/app/components/feed/feed.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @{{feed.user.screen_name}}
5 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/src/app/components/feed/feed.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Output, EventEmitter } from '@angular/core';
2 | import { Tweet } from '../../models'
3 |
4 | declare var $: any;
5 |
6 | @Component({
7 | selector: 'ist-feed',
8 | templateUrl: './feed.component.html',
9 | styleUrls: ['./feed.component.css']
10 | })
11 | export class FeedComponent {
12 | @Input() feed: any;
13 | @Output() favClicked = new EventEmitter();
14 | @Output() removeFavClicked = new EventEmitter();
15 | @Output() retweetClicked = new EventEmitter();
16 | /**Add to fav
17 | *
18 | * Note: to Fav a tweet it needs a `id_str` not `id` of a tweet
19 | */
20 | addtoFavClicked(){
21 | this.favClicked.emit(this.feed);
22 | }
23 |
24 | removeFromFavClicked(){
25 | this.removeFavClicked.emit(this.feed);
26 | }
27 |
28 | retweetBtnClicked(){
29 | this.retweetClicked.emit(this.feed);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/components/feeds/feeds.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/app/components/feeds/feeds.component.css
--------------------------------------------------------------------------------
/src/app/components/feeds/feeds.component.html:
--------------------------------------------------------------------------------
1 |
12 |
13 |
--------------------------------------------------------------------------------
/src/app/components/feeds/feeds.component.ts:
--------------------------------------------------------------------------------
1 | import { Component,OnInit } from '@angular/core';
2 | import { ApiService } from '../../services/api.service';
3 | import { Store } from '@ngrx/store';
4 | import { AppState, getAllFeeds } from '../../reducers';
5 | import { FeedsActions } from '../../actions/feeds.actions';
6 | import { Observable } from 'rxjs/Observable';
7 | import { Router } from '@angular/router';
8 |
9 | declare var $: any;
10 |
11 | @Component({
12 | selector: 'ist-feeds',
13 | templateUrl: './feeds.component.html',
14 | styleUrls: ['./feeds.component.css']
15 | })
16 | export class FeedsComponent implements OnInit {
17 | feeds: Observable;
18 | constructor(private store: Store,
19 | private feedActions: FeedsActions,
20 | private router: Router) {
21 | this.feeds = this.store.select(getAllFeeds);
22 | }
23 |
24 | ngOnInit() {
25 | this.store.dispatch(this.feedActions.getAllFeeds());
26 | }
27 |
28 | /**
29 | * Dispatch a store action to add a particular
30 | * feed to fav
31 | *
32 | * @param feed
33 | */
34 | addToFav(feed){
35 | this.store.dispatch(this.feedActions.addFeedToFav(feed));
36 | }
37 |
38 | /**
39 | * Dispatch a store action to remove a particular
40 | * feed from fav
41 | *
42 | * @param feed
43 | */
44 | removeFromFav(feed){
45 | this.store.dispatch(this.feedActions.removeFeedFromFav(feed));
46 | }
47 |
48 | /**
49 | * Dispatch a store action to retweet a particular
50 | * feed
51 | *
52 | * @param feed
53 | */
54 | retweet(feed){
55 | this.store.dispatch(this.feedActions.retweet(feed));
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/app/components/index.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { ReactiveFormsModule } from '@angular/forms';
4 | import { RouterModule } from '@angular/router';
5 | import { PipesModule } from '../pipes';
6 |
7 | /** Components */
8 | import { FeedComponent } from './feed/feed.component';
9 | import { SuggestedListsComponent } from './suggested-lists/suggested-lists.component';
10 | import { FeedDetailComponent } from './feed-detail/feed-detail.component';
11 | import { ModalComponent } from './shared/modal/modal.component';
12 | import { FeedsComponent } from './feeds/feeds.component';
13 | import { FdSummaryComponent } from './feed-detail/fd-summary/fd-summary.component';
14 |
15 | export const COMPONENTS = [
16 | FeedComponent,
17 | FeedsComponent,
18 | ModalComponent,
19 | FeedDetailComponent,
20 | SuggestedListsComponent,
21 | FdSummaryComponent
22 | ];
23 |
24 | @NgModule({
25 | imports: [
26 | CommonModule,
27 | ReactiveFormsModule,
28 | RouterModule,
29 | PipesModule
30 | ],
31 | declarations: COMPONENTS,
32 | exports: COMPONENTS
33 | })
34 | export class ComponentsModule { }
--------------------------------------------------------------------------------
/src/app/components/shared/modal/modal.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/app/components/shared/modal/modal.component.css
--------------------------------------------------------------------------------
/src/app/components/shared/modal/modal.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/app/components/shared/modal/modal.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Component,
3 | AfterViewInit,
4 | trigger,
5 | state,
6 | style,
7 | transition,
8 | animate
9 | } from '@angular/core';
10 |
11 | @Component({
12 | selector: 'ist-modal',
13 | templateUrl: './modal.component.html',
14 | styleUrls: ['./modal.component.css'],
15 | animations: [
16 | trigger('flyInUp', [
17 | state('in', style({ transform: 'translateY(-50%)' })),
18 | transition('void => *', [
19 | style({ transform: 'translateY(100%)' }),
20 | animate(500)
21 | ]),
22 | transition('* => void', [
23 | animate(500, style({ transform: 'translateY(-100%)' }))
24 | ])
25 | ])
26 | ]
27 | })
28 | export class ModalComponent {
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/components/suggested-lists/suggested-lists.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/app/components/suggested-lists/suggested-lists.component.css
--------------------------------------------------------------------------------
/src/app/components/suggested-lists/suggested-lists.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Joined in 2013
5 |
6 |
7 |
8 | {{ list.description}}
9 |
10 |
11 | Amazing collection of {{ list.name }} coders
12 |
13 |
14 |
15 |
16 | Follow
17 |
18 |
19 | UnFollow
20 |
--------------------------------------------------------------------------------
/src/app/components/suggested-lists/suggested-lists.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, EventEmitter, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'ist-suggested-lists',
5 | templateUrl: './suggested-lists.component.html',
6 | styleUrls: ['./suggested-lists.component.css']
7 | })
8 | export class SuggestedListsComponent {
9 | @Input() list;
10 | @Output() followClicked = new EventEmitter();
11 | @Output() unFollowClicked = new EventEmitter();
12 |
13 | followList(){
14 | this.followClicked.emit(this.list.id);
15 | }
16 |
17 | unFollowList(){
18 | this.unFollowClicked.emit(this.list.id);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/containers/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/app/containers/app.component.css
--------------------------------------------------------------------------------
/src/app/containers/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/app/containers/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { Store } from '@ngrx/store';
4 | import { AppState, getLoginState } from '../reducers/index';
5 | import { LoginActions } from '../actions/login.actions';
6 | import { UserAuthService } from '../services/user-auth.service';
7 |
8 |
9 | @Component({
10 | selector: 'ist-root',
11 | templateUrl: './app.component.html',
12 | styleUrls: ['./app.component.css']
13 | })
14 | export class AppComponent {
15 | constructor(private router: Router,
16 | private loginActions: LoginActions,
17 | private store: Store,
18 | private api: UserAuthService){
19 |
20 | this.store.let(getLoginState())
21 | .filter(state => state.server_token !== null)
22 | .subscribe(() => this.router.navigate(['/dashboard']));
23 | }
24 | ngOnInit(){
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/containers/dashboard-page/dashboard-page.component.css:
--------------------------------------------------------------------------------
1 | /*
2 | Make Background light
3 | Greyish and make it acquire full height
4 | */
5 | .dashboard {
6 | background: #F1F3FA;
7 | height: 100vh;
8 | }
9 |
10 | .profile {
11 | margin: 20px 0;
12 | }
13 |
14 | .profile-sidebar {
15 | padding: 20px 0 10px 0;
16 | background: #fff;
17 | }
18 |
19 | /*SideBar UserPic*/
20 | .profile-userpic img {
21 | float: none;
22 | margin: 0 auto;
23 | width: 50%;
24 | height: 50%;
25 | -webkit-border-radius: 50% !important;
26 | -moz-border-radius: 50% !important;
27 | border-radius: 50% !important;
28 | }
29 |
30 |
31 | .profile-usertitle {
32 | text-align: center;
33 | margin-top: 20px;
34 | }
35 |
36 | .profile-usertitle-name {
37 | color: #5a7391;
38 | font-size: 16px;
39 | font-weight: 600;
40 | margin-bottom: 7px;
41 | }
42 |
43 | .item::hover{
44 | cursor: pointer;
45 | }
46 |
47 | a:hover {
48 | cursor: pointer;
49 | }
50 |
51 | .list-link {
52 | padding: 10px 0px 10px 5px;
53 | }
54 |
55 | .list-link:hover{
56 | cursor: pointer;
57 | }
--------------------------------------------------------------------------------
/src/app/containers/dashboard-page/dashboard-page.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/containers/dashboard-page/dashboard-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Store } from '@ngrx/store';
5 | import { AppState, getUserList, getUserState } from '../../reducers/index';
6 | import { UserActions } from '../../actions/user.actions';
7 | import { LoginActions } from '../../actions/login.actions';
8 | import { User } from '../../models/';
9 | import { UserListActions } from '../../actions/user-list.actions';
10 | import { go } from '@ngrx/router-store';
11 |
12 | @Component({
13 | selector: 'ist-dashboard-page',
14 | templateUrl: './dashboard-page.component.html',
15 | styleUrls: ['./dashboard-page.component.css']
16 | })
17 | export class DashboardPageComponent implements OnInit {
18 | lists;
19 | userList$: Observable;
20 | user$: Observable;
21 | constructor(private router: Router,
22 | private userActions: UserActions,
23 | private loginActions: LoginActions,
24 | private userListActions: UserListActions,
25 | private store: Store) {
26 | this.userList$ = this.store.select(getUserList);
27 | this.user$ = this.store.select(getUserState);
28 | }
29 |
30 | signOutUser(){
31 | this.store.dispatch(this.loginActions.logout());
32 | }
33 |
34 | ngOnInit() {
35 | this.store.dispatch(this.userListActions.getUserLists());
36 | this.store.dispatch(this.userActions.loadProfile());
37 | }
38 |
39 | backToHome(){
40 | this.store.dispatch(go(['/dashboard/feeds']))
41 | }
42 |
43 | goToSuggestionPage(){
44 | this.store.dispatch(go('/dashboard/suggestions'))
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/containers/list-feed-page/list-feed-page.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/app/containers/list-feed-page/list-feed-page.component.css
--------------------------------------------------------------------------------
/src/app/containers/list-feed-page/list-feed-page.component.html:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/app/containers/list-feed-page/list-feed-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router, ActivatedRoute } from '@angular/router';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Store } from '@ngrx/store';
5 | import { AppState, getSelectedUserListIdFeeds } from '../../reducers/index';
6 | import { Subscription } from 'rxjs';
7 | import { FeedsActions } from '../../actions/feeds.actions';
8 |
9 | @Component({
10 | selector: 'ist-list-feed-page',
11 | templateUrl: './list-feed-page.component.html',
12 | styleUrls: ['./list-feed-page.component.css']
13 | })
14 | export class ListFeedPageComponent implements OnInit {
15 | private subscription: Subscription;
16 | private userListId: string;
17 | feeds: Observable;
18 |
19 | constructor(private router: Router,
20 | private route: ActivatedRoute,
21 | private store: Store,
22 | private feedsActions: FeedsActions) {
23 | this.feeds = this.store.select(getSelectedUserListIdFeeds);
24 | }
25 |
26 | ngOnInit() {
27 | this.subscription = this.route.params.subscribe(
28 | (params: any) => {
29 | this.userListId = params['id'];
30 | this.store.dispatch(this.feedsActions.getFeedsForId(this.userListId));
31 | })
32 | }
33 |
34 | /**
35 | * Dispatch a store action to add a particular
36 | * feed to fav
37 | *
38 | * @param feed
39 | */
40 | addToFav(feed){
41 | this.store.dispatch(this.feedsActions.addFeedToFav(feed));
42 | }
43 |
44 | /**
45 | * Dispatch a store action to remove a particular
46 | * feed from fav
47 | *
48 | * @param feed
49 | */
50 | removeFromFav(feed){
51 | this.store.dispatch(this.feedsActions.removeFeedFromFav(feed));
52 | }
53 |
54 | /**
55 | * Dispatch a store action to retweet a particular
56 | * feed
57 | *
58 | * @param feed
59 | */
60 | retweet(feed){
61 | this.store.dispatch(this.feedsActions.retweet(feed));
62 | }
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/app/containers/login-page/login-page.component.css:
--------------------------------------------------------------------------------
1 | .lightbox {
2 | position: fixed;
3 | top: 0px;
4 | left: 0px;
5 | width: 100%;
6 | height: 100%;
7 | background: lightblue;
8 | text-align: center;
9 | }
10 |
11 | .tc-content {
12 | position: relative;
13 | top: 30%;
14 | }
--------------------------------------------------------------------------------
/src/app/containers/login-page/login-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Listify
6 |
7 |
8 |
9 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/src/app/containers/login-page/login-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs';
4 | import { Store } from '@ngrx/store';
5 | import { AppState, getLoginState } from '../../reducers/index';
6 | import { LoginActions } from '../../actions/login.actions';
7 |
8 | @Component({
9 | selector: 'ist-login-page',
10 | templateUrl: './login-page.component.html',
11 | styleUrls: ['./login-page.component.css'],
12 | changeDetection: ChangeDetectionStrategy.OnPush,
13 | providers: [LoginActions]
14 | })
15 | export class LoginPageComponent implements OnInit {
16 | photo;
17 | constructor(private loginActions: LoginActions,
18 | private store: Store) {
19 | }
20 |
21 | ngOnInit() {
22 | }
23 |
24 | login(){
25 | this.store.dispatch(this.loginActions.login('twitter'))
26 | }
27 | signUp(){
28 | this.store.dispatch(this.loginActions.signUp('twitter'))
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/src/app/containers/signup-page/signu-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3 | import {Observable} from 'rxjs/Observable';
4 | import 'rxjs';
5 | import { Store } from '@ngrx/store';
6 | import { AppState,getLoginState } from '../../reducers/index';
7 | import { LoginActions } from '../../actions/login.actions';
8 |
9 | @Component({
10 | selector: 'ist-signup-page',
11 | templateUrl: './signup-page.component.html',
12 | styleUrls: ['./signup-page.component.css']
13 | })
14 | export class SignUpPageComponent implements OnInit {
15 | emailForm: FormGroup;
16 | email;
17 | userAuth: any;
18 | constructor(private fb: FormBuilder,
19 | private loginActions: LoginActions,
20 | private store: Store) {
21 | this.store.select(state => this.userAuth = state.userAuth).subscribe()
22 | }
23 |
24 | ngOnInit() {
25 | this.emailForm = this.fb.group({
26 | 'email': ['', Validators.required]
27 | });
28 | }
29 |
30 | login() {
31 | this.store.dispatch(this.loginActions.storeUser(this.email, this.userAuth))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/containers/signup-page/signup-page.component.css:
--------------------------------------------------------------------------------
1 | .lightbox {
2 | position: fixed;
3 | top: 0px;
4 | left: 0px;
5 | width: 100%;
6 | height: 100%;
7 | background: lightblue;
8 | text-align: center;
9 | }
10 |
11 | .tc-content {
12 | position: relative;
13 | top: 30%;
14 | }
--------------------------------------------------------------------------------
/src/app/containers/signup-page/signup-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Please Enter your Email Address
4 |
15 |
16 |
--------------------------------------------------------------------------------
/src/app/containers/suggestions-page/suggestions-page.component.css:
--------------------------------------------------------------------------------
1 | .card {
2 |
3 | }
--------------------------------------------------------------------------------
/src/app/containers/suggestions-page/suggestions-page.component.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/src/app/containers/suggestions-page/suggestions-page.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ApiService } from '../../services/api.service';
3 | import { Router } from '@angular/router';
4 | import { Observable } from 'rxjs/Observable';
5 | import { Store } from '@ngrx/store';
6 | import { AppState, getLists } from '../../reducers/index';
7 | import { ListActions } from '../../actions/list.actions';
8 | import { List } from '../../models/list';
9 |
10 | @Component({
11 | selector: 'ist-suggestions-page',
12 | templateUrl: './suggestions-page.component.html',
13 | styleUrls: ['./suggestions-page.component.css'],
14 | providers: [ApiService]
15 | })
16 | export class SuggestionsPageComponent implements OnInit {
17 | suggestedList$: Observable;
18 |
19 | constructor(private api: ApiService,
20 | private router: Router,
21 | private listActions: ListActions,
22 | private store: Store) {
23 | this.suggestedList$ = this.store.select(getLists);
24 | }
25 |
26 | ngOnInit() {
27 | this.store.dispatch(this.listActions.retriveLists())
28 | }
29 |
30 | follow(listId){
31 | this.store.dispatch(this.listActions.follow(listId));
32 | }
33 |
34 | unfollow(listId){
35 | this.store.dispatch(this.listActions.unFollowList(listId));
36 | }
37 |
38 | goToFeedsDashboard(){
39 | this.router.navigate(['/dashboard']);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/effects/feeds.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Effect, Actions } from '@ngrx/effects';
3 | import { ActionTypes, FeedsActions } from '../actions/feeds.actions';
4 | import { Action } from '@ngrx/store';
5 | import { ApiService } from '../services/api.service';
6 | import { ResponseParseService } from '../services/response-parse.service';
7 | import { Tweet } from '../models';
8 |
9 | @Injectable()
10 | export class FeedsEffects {
11 | constructor(private actions$: Actions,
12 | private feedsActions: FeedsActions,
13 | private apiService: ApiService,
14 | private responseParser: ResponseParseService
15 | ) { }
16 |
17 | @Effect() getFeedsForId$ = this.actions$
18 | .ofType(ActionTypes.GET_FEEDS_FOR_ID)
19 | .map((action: Action) => action.payload)
20 | .switchMap((userListId: any) => this.apiService.getListsTimeLine(userListId))
21 | .map((response: any) => this.responseParser.createTweetsObj(response))
22 | .map((tweets: Tweet[]) => this.feedsActions.getFeedsForIdSuccess(tweets));
23 |
24 |
25 | @Effect() getAllFeeds$ = this.actions$
26 | .ofType(ActionTypes.GET_ALL_FEEDS)
27 | .switchMap(() => this.apiService.all_feeds())
28 | .map((response: any) => this.responseParser.createTweetsObj(response))
29 | .map((tweets: Tweet[]) => this.feedsActions.getAllFeedsSuccess(tweets));
30 |
31 | @Effect() addFeedToFav$ = this.actions$
32 | .ofType(ActionTypes.ADD_FEED_TO_FAV)
33 | .map((action: Action) => action.payload)
34 | .switchMap((feed: any) => this.apiService.addToFav(feed))
35 | .map((response: any) => this.feedsActions.addFeedToFavSuccess(response.feed));
36 |
37 | @Effect() removeFeedFromFav$ = this.actions$
38 | .ofType(ActionTypes.REMOVE_FEED_FROM_FAV)
39 | .map((action: Action) => action.payload)
40 | .switchMap((feed: any) => this.apiService.removeFromFav(feed))
41 | .map((response: any) => this.feedsActions.removeFeedFromFavSuccess(response.feed));
42 |
43 | @Effect() retweet$ = this.actions$
44 | .ofType(ActionTypes.RETWEET)
45 | .map((action: Action) => action.payload)
46 | .switchMap((feed: any) => this.apiService.retweet(feed))
47 | .map((response: any) => this.feedsActions.retweetSuccess(response.feed));
48 |
49 | @Effect() reply$ = this.actions$
50 | .ofType(ActionTypes.REPLY)
51 | .map((action: Action) => action.payload)
52 | .switchMap((messageWithFeed: any) => this.apiService.reply(messageWithFeed))
53 | .map((response: any) => this.feedsActions.replySuccess(response.feed));
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/effects/list.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Effect, Actions } from '@ngrx/effects';
3 | import { ActionTypes, ListActions } from '../actions/list.actions';
4 | import { Action } from '@ngrx/store';
5 | import { ApiService } from '../services/api.service';
6 | import { ResponseParseService } from '../services/response-parse.service';
7 | import { List } from '../models';
8 |
9 | @Injectable()
10 | export class ListEffects {
11 | constructor(
12 | private actions$: Actions,
13 | private listActions: ListActions,
14 | private apiService: ApiService,
15 | private responseParser: ResponseParseService
16 | ){ }
17 |
18 | @Effect() retriveLists$ = this.actions$
19 | .ofType(ActionTypes.RETRIVE_LISTS)
20 | .switchMap(() => this.apiService.retriveSuggestion())
21 | .filter(response => response !== null )
22 | .map(response => this.responseParser.createSuggestedListsObj(response))
23 | .map((lists: List[]) => this.listActions.retriveListsSuccess(lists));
24 |
25 | @Effect() followList$ = this.actions$
26 | .ofType(ActionTypes.FOLLOW_LIST)
27 | .map((action: Action) => action.payload)
28 | .switchMap((listId: string) => this.apiService.followList(listId))
29 | .map((response) => this.listActions.followSuccess(response))
30 |
31 |
32 | @Effect() followListSuccess$ = this.actions$
33 | .ofType(ActionTypes.FOLLOW_LIST_SUCCESS)
34 | .map((action: Action) => action.payload.new_list)
35 | .map((new_list: any) => this.responseParser.createSuggestedListsObj(new_list))
36 | .map((updatedLists: List[]) => this.listActions.updateLists(updatedLists));
37 |
38 | @Effect() unFollowList$ = this.actions$
39 | .ofType(ActionTypes.UNFOLLOW_LIST)
40 | .map((action: Action) => action.payload)
41 | .switchMap((listId: string) => this.apiService.unFollowList(listId))
42 | .map((response: any) => this.listActions.unFollowListSuccess(response));
43 | }
44 |
--------------------------------------------------------------------------------
/src/app/effects/user-auth.effect.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Effect, Actions } from '@ngrx/effects';
3 | import { ActionTypes, LoginActions } from '../actions/login.actions';
4 | import { Action } from '@ngrx/store';
5 | import { UserAuthService } from '../services/user-auth.service';
6 | import { UserAuth } from '../models';
7 | import { go } from '@ngrx/router-store';
8 |
9 | @Injectable()
10 | export class UserAuthEffects {
11 | constructor(private actions$: Actions,
12 | private loginActions: LoginActions,
13 | private userAuthService: UserAuthService
14 | ){}
15 |
16 | @Effect() login$ = this.actions$
17 | .ofType(ActionTypes.LOGIN)
18 | .map((action: Action) => action.payload)
19 | .switchMap((payload: any) => this.userAuthService.login())
20 | .map((userAuth: UserAuth) => {
21 | this.userAuthService.storeUserAuthInLocalstorage(userAuth);
22 | return this.loginActions.loginSuccess(userAuth);
23 | } );
24 |
25 | @Effect() loginSuccess$ = this.actions$
26 | .ofType(ActionTypes.LOGIN_SUCCESS)
27 | .map((action: Action) => action.payload)
28 | .switchMap((userAuth: UserAuth) => this.userAuthService.loginServer(userAuth))
29 | .map((userAuth: UserAuth) => {
30 | if(userAuth.server_token){
31 | this.userAuthService.storeServerToken(userAuth.server_token);
32 | return this.loginActions.loginServerSuccess(userAuth);
33 | } else{
34 | // User is Not present but he has authrised his twitter
35 | // account with our app hence taking him to request email page
36 | return this.loginActions.signUpSuccess(userAuth);
37 | }
38 | })
39 |
40 | @Effect() signup$ = this.actions$
41 | .ofType(ActionTypes.SIGNUP)
42 | .map((action: Action) => action.payload)
43 | .switchMap((payload) => this.userAuthService.signUp())
44 | .map((userAuth: UserAuth) => {
45 | this.userAuthService.storeUserAuthInLocalstorage(userAuth);
46 | return this.loginActions.signUpSuccess(userAuth);
47 | } );
48 |
49 |
50 | @Effect() signUpSucces$ = this.actions$
51 | .ofType(ActionTypes.SIGNUP_SUCCESS)
52 | .map(() => go(['/request-email']));
53 |
54 |
55 | @Effect() storeUser$ = this.actions$
56 | .ofType(ActionTypes.STORE_USER)
57 | .map((action: Action) => action.payload)
58 | .switchMap((payload) => this.userAuthService.storeUsertoBackend(payload))
59 | .map((userAuth) => {
60 | this.userAuthService.storeServerToken(userAuth.server_token);
61 | return this.loginActions.storeUserSuccess(userAuth)
62 | })
63 |
64 | @Effect() storeUserSuccess$ = this.actions$
65 | .ofType(ActionTypes.STORE_USER_SUCCESS)
66 | .map(() => go(['/dashboard/suggestions']));
67 |
68 | @Effect() logout$ = this.actions$
69 | .ofType(ActionTypes.LOGOUT)
70 | .switchMap(() => this.userAuthService.logout())
71 | .map(() => this.loginActions.logoutSuccess(UserAuth))
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/app/effects/user-lists.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Effect, Actions } from '@ngrx/effects';
3 | import { ActionTypes,UserListActions } from '../actions/user-list.actions';
4 | import { Action } from '@ngrx/store';
5 | import { ApiService } from '../services/api.service';
6 | import { ResponseParseService } from '../services/response-parse.service';
7 | import { UserList } from '../models';
8 | import {Observable} from 'rxjs/Observable';
9 |
10 | @Injectable()
11 | export class UserListsEffects {
12 | constructor(
13 | private actions$: Actions,
14 | private userListActions: UserListActions,
15 | private apiService: ApiService,
16 | private reponseParser: ResponseParseService
17 | ){ }
18 |
19 | @Effect() getUserLists$ = this.actions$
20 | .ofType(ActionTypes.GET_USER_LISTS)
21 | .switchMap(() => this.apiService.getUserLists())
22 | .map((response:any) => this.reponseParser.createUserListsobj(response))
23 | .map((userLists: UserList[]) => this.userListActions.getUserListsSuccess(userLists));
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/effects/user.effects.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Effect, Actions } from '@ngrx/effects';
3 | import { ActionTypes, UserActions } from '../actions/user.actions';
4 | import { Action } from '@ngrx/store';
5 | import { ApiService } from '../services/api.service';
6 | import { ResponseParseService } from '../services/response-parse.service';
7 | import { User } from '../models';
8 |
9 | @Injectable()
10 | export class UserEffects {
11 |
12 | constructor(
13 | private actions$: Actions,
14 | private userActions: UserActions,
15 | private api: ApiService,
16 | private responseParser: ResponseParseService
17 | ){ }
18 |
19 | @Effect() loadUserProfile$ = this.actions$
20 | .ofType(ActionTypes.LOAD_PROFILE)
21 | .switchMap(() => this.api.getUserDetail())
22 | .map((response:any) => this.responseParser.createUserObj(response))
23 | .map((user: User) => this.userActions.loadProfileSuccess(user))
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from './containers/app.component';
2 | export * from './app.module';
3 |
--------------------------------------------------------------------------------
/src/app/models/base.ts:
--------------------------------------------------------------------------------
1 | export class Base {
2 | public id: any;
3 |
4 | constructor(attributes?) {
5 | if(attributes){
6 | let keys = Object.keys(attributes)
7 | if (keys.length) {
8 | keys.forEach(el => {
9 | this[el] = attributes[el];
10 | })
11 | }
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/app/models/entities.ts:
--------------------------------------------------------------------------------
1 | import { Urls } from './urls';
2 | import { Base } from './base';
3 | export class Entities extends Base{
4 | public hashtags: Array;
5 | public symbols: Array;
6 | public urls: Urls[]
7 | }
--------------------------------------------------------------------------------
/src/app/models/index.ts:
--------------------------------------------------------------------------------
1 | export { UserAuth } from './user-auth';
2 | export { UserProfile } from './user-profile';
3 | export { User } from './user';
4 | export { List } from './list';
5 | export { UserList } from './user-list';
6 | export { Tweet } from './tweet';
7 | export { Entities } from './entities'
8 | export { Urls } from './urls';
9 | export { Base } from './base';
--------------------------------------------------------------------------------
/src/app/models/list.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './base';
2 |
3 | export class List extends Base {
4 | name: string;
5 | description: string;
6 | image_url: string;
7 | isFollowing: boolean;
8 | }
--------------------------------------------------------------------------------
/src/app/models/tweet.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './base';
2 | import { User } from './user';
3 | import { Entities } from './entities';
4 | export class Tweet extends Base {
5 | created_at: string
6 | text: string;
7 | retweet_count: number;
8 | favorite_count: number;
9 | user: User;
10 | user_list_id: string;
11 | entities: Entities;
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/models/urls.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './base';
2 |
3 | export class Urls extends Base {
4 | url: string;
5 | expanded_url: string;
6 | display_url: string;
7 | indices: Array;
8 | }
--------------------------------------------------------------------------------
/src/app/models/user-auth.ts:
--------------------------------------------------------------------------------
1 | export class UserAuth {
2 | constructor(
3 | public user_id: string = localStorage.getItem('user_id'),
4 | public access_token: string = localStorage.getItem('access_token'),
5 | public secret_token: string = localStorage.getItem('secret_token'),
6 | public server_token: string = localStorage.getItem('server_token')
7 | ) { }
8 | }
--------------------------------------------------------------------------------
/src/app/models/user-list.ts:
--------------------------------------------------------------------------------
1 | import { Base } from './base';
2 |
3 | export class UserList extends Base {
4 | list_id: any;
5 | twitter_list_id: string;
6 | slug: string;
7 | name: string;
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/models/user-profile.ts:
--------------------------------------------------------------------------------
1 | export class UserProfile {
2 | constructor(public first_name: string = null,
3 | public last_name: string = null) {}
4 | }
--------------------------------------------------------------------------------
/src/app/models/user.ts:
--------------------------------------------------------------------------------
1 | import { UserProfile } from './user-profile';
2 | import { Base } from './base';
3 | import { Entities } from './entities';
4 |
5 | export class User extends Base {
6 | name: string;
7 | screen_name: string;
8 | profile_image_url: string;
9 | description: string;
10 | location: string;
11 | entities: Entities;
12 | }
--------------------------------------------------------------------------------
/src/app/pipes/capitalize.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({ name: 'capitalize' })
4 | export class CapitalizePipe implements PipeTransform {
5 |
6 | transform(value: any) {
7 | if (value) {
8 | return value.charAt(0).toUpperCase() + value.slice(1);
9 | }
10 | return value;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/src/app/pipes/index.ts:
--------------------------------------------------------------------------------
1 | import { MentionsPipe } from './mentions';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { LinkifyPipe } from './linkify';
5 | import { CapitalizePipe } from './capitalize';
6 | import { StringifyPipe } from './stringify';
7 |
8 | export const PIPES = [
9 | LinkifyPipe,
10 | CapitalizePipe,
11 | StringifyPipe,
12 | MentionsPipe
13 | ];
14 |
15 | @NgModule({
16 | declarations: PIPES,
17 | exports: PIPES
18 | })
19 | export class PipesModule { }
--------------------------------------------------------------------------------
/src/app/pipes/linkify.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { Urls } from '../models';
3 |
4 | @Pipe({name: 'linkify'})
5 | export class LinkifyPipe implements PipeTransform {
6 |
7 | transform(text: string, urls: Urls[] ): string {
8 | return this.linkify(text, urls)
9 | }
10 |
11 | private linkify(text: string, urls: Urls[]): any {
12 | var replacedText = text;
13 | urls.forEach(el => {
14 | replacedText = replacedText.replace(el.url, '' + el.display_url + '')
15 | })
16 | return replacedText;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/app/pipes/mentions.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | /**
4 | * Converts a array of mentions into a string
5 | * to show in input box of reply
6 | *
7 | * @param { Array } mentions
8 | * E.g : [{sceen_name: 'coderaga'}, {screen_name: '_voidzero'}]
9 | *
10 | * @return A String with all mentions in it if any
11 | * E.g : "@coderaga @_voidzero"
12 | */
13 | @Pipe({name: 'mentions'})
14 | export class MentionsPipe implements PipeTransform {
15 | transform(mentions: Array, ownerScreenName: string){
16 |
17 | var text = "";
18 | var newMentions = this.getAllMentionsArray(mentions, ownerScreenName)
19 |
20 | newMentions.forEach(el => {
21 | text = text + "@" + el.screen_name + " ";
22 | })
23 | return text;
24 | }
25 |
26 | private getAllMentionsArray(mentions, ownerScreenName){
27 | var ownerScreenNameObj = { screen_name: ownerScreenName };
28 | if(mentions){
29 | mentions.push(ownerScreenNameObj);
30 | return mentions;
31 | }
32 | else{
33 | var newMentions = [];
34 | newMentions.push(ownerScreenNameObj);
35 | return newMentions;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/app/pipes/stringify.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 |
4 | @Pipe({ name: 'stringify' })
5 | export class StringifyPipe implements PipeTransform {
6 | transform(value: any) {
7 | if (value) {
8 | return value.replace(/\d+|[-_]/g, '');
9 | }
10 | return value;
11 | }
12 | }
--------------------------------------------------------------------------------
/src/app/reducers/feeds.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action } from '@ngrx/store';
2 | import { Tweet } from '../models/';
3 | import { ActionTypes } from '../actions/feeds.actions';
4 |
5 | export type State = {
6 | ids: string[];
7 | entities: { [id: string]: any };
8 | selectedUserListId: string;
9 | selectedFeedId: string;
10 | }
11 |
12 | const initialState: State = {
13 | ids: [],
14 | entities: {},
15 | selectedUserListId: null,
16 | selectedFeedId: null
17 | }
18 |
19 | export default function(state = initialState, action: Action): State {
20 | switch(action.type){
21 | case ActionTypes.GET_FEEDS_FOR_ID: {
22 | return Object.assign({}, state, {
23 | selectedUserListId: action.payload
24 | });
25 | }
26 |
27 | case ActionTypes.GET_FEEDS_FOR_ID_SUCCESS: {
28 | const tweets: Tweet[] = action.payload
29 |
30 | // filter all new tweets and ids
31 | /**
32 | * TODO: Rethink over this approach
33 | */
34 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
35 |
36 | const newTweets: any = tweets;
37 |
38 | const newTweetIds = tweets
39 | .filter(tweet => !state.entities[tweet.id])
40 | .map(tweet => tweet.id);
41 |
42 | const newEntities = newTweets
43 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
44 | return Object.assign(entities, { [tweet.id]: tweet })
45 | }, {});
46 |
47 | return Object.assign({}, state, {
48 | ids: [...state.ids, ...newTweetIds],
49 | entities: Object.assign({}, state.entities, newEntities)
50 | })
51 | }
52 |
53 | case ActionTypes.GET_ALL_FEEDS_SUCCESS: {
54 | const tweets: any = action.payload
55 | /**
56 | * TODO: Rethink over this approach
57 | */
58 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
59 |
60 | const newTweets: any = tweets;
61 |
62 | const newTweetIds = tweets
63 | .filter(tweet => !state.entities[tweet.id])
64 | .map(tweet => tweet.id);
65 |
66 | const newEntities = newTweets
67 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
68 | return Object.assign(entities, { [tweet.id]: tweet })
69 | }, {});
70 |
71 | return Object.assign({}, state, {
72 | ids: [...state.ids, ...newTweetIds],
73 | entities: Object.assign({}, state.entities, newEntities)
74 | })
75 | }
76 |
77 | case ActionTypes.SELECT_FEED: {
78 | return Object.assign({}, state, {
79 | selectedFeedId: action.payload
80 | });
81 | }
82 |
83 | case ActionTypes.ADD_FEED_TO_FAV_SUCCESS: {
84 | const tweets: any = action.payload
85 | /**
86 | * TODO: Rethink over this approach
87 | */
88 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
89 |
90 | const newTweets: any = tweets;
91 |
92 | const newTweetIds = tweets
93 | .filter(tweet => !state.entities[tweet.id])
94 | .map(tweet => tweet.id);
95 |
96 | const newEntities = newTweets
97 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
98 | return Object.assign(entities, { [tweet.id]: tweet })
99 | }, {});
100 |
101 | return Object.assign({}, state, {
102 | ids: [...state.ids, ...newTweetIds],
103 | entities: Object.assign({}, state.entities, newEntities)
104 | })
105 | }
106 |
107 | case ActionTypes.REMOVE_FEED_FROM_FAV_SUCESS: {
108 | const tweets: any = action.payload
109 | /**
110 | * TODO: Rethink over this approach
111 | */
112 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
113 |
114 | const newTweets: any = tweets;
115 |
116 | const newTweetIds = tweets
117 | .filter(tweet => !state.entities[tweet.id])
118 | .map(tweet => tweet.id);
119 |
120 | const newEntities = newTweets
121 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
122 | return Object.assign(entities, { [tweet.id]: tweet })
123 | }, {});
124 |
125 | return Object.assign({}, state, {
126 | ids: [...state.ids, ...newTweetIds],
127 | entities: Object.assign({}, state.entities, newEntities)
128 | })
129 | }
130 |
131 | case ActionTypes.RETWEET_SUCCESS: {
132 | const tweets: any = action.payload
133 | /**
134 | * TODO: Rethink over this approach
135 | */
136 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
137 |
138 | const newTweets: any = tweets;
139 |
140 | const newTweetIds = tweets
141 | .filter(tweet => !state.entities[tweet.id])
142 | .map(tweet => tweet.id);
143 |
144 | const newEntities = newTweets
145 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
146 | return Object.assign(entities, { [tweet.id]: tweet })
147 | }, {});
148 |
149 | return Object.assign({}, state, {
150 | ids: [...state.ids, ...newTweetIds],
151 | entities: Object.assign({}, state.entities, newEntities)
152 | })
153 | }
154 |
155 | case ActionTypes.REPLY_SUCCESS : {
156 | const tweets: any = action.payload
157 | /**
158 | * TODO: Rethink over this approach
159 | */
160 | // const newTweets: any = tweets.filter(tweet => !state.entities[tweet.id]);
161 |
162 | const newTweets: any = tweets;
163 |
164 | const newTweetIds = tweets
165 | .filter(tweet => !state.entities[tweet.id])
166 | .map(tweet => tweet.id);
167 |
168 | const newEntities = newTweets
169 | .reduce((entities: { [id: string]: Tweet }, tweet: Tweet) => {
170 | return Object.assign(entities, { [tweet.id]: tweet })
171 | }, {});
172 |
173 | return Object.assign({}, state, {
174 | ids: [...state.ids, ...newTweetIds],
175 | entities: Object.assign({}, state.entities, newEntities)
176 | })
177 | }
178 |
179 | default: {
180 | return state;
181 | }
182 | }
183 | }
184 |
185 | export const getIds = (state: State) => state.ids;
186 | export const getEntities = (state: State) => state.entities;
187 | export const getSelectedUserListId = (state: State) => state.selectedUserListId;
188 | export const getSelectedFeedId = (state: State) => state.selectedFeedId;
189 |
--------------------------------------------------------------------------------
/src/app/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import '@ngrx/core/add/operator/select';
2 | import 'rxjs/add/operator/let';
3 | import { compose } from '@ngrx/core/compose';
4 | import { combineReducers } from '@ngrx/store';
5 | import { Observable } from 'rxjs/Observable';
6 | import { RouterState, routerReducer } from '@ngrx/router-store';
7 | import { createSelector } from 'reselect';
8 |
9 | import userAuth, * as fromUserAuth from './user-auth.reducer';
10 | import user, * as fromUser from './user.reducer';
11 | import userList, * as fromUserList from './user-list.reducer';
12 | import lists, * as fromLists from './list.reducer';
13 | import feeds, * as fromFeeds from './feeds.reducer';
14 |
15 | // Entire State of a App
16 | export interface AppState {
17 | userAuth: fromUserAuth.AuthState;
18 | user: fromUser.UserState;
19 | userList: fromUserList.State;
20 | lists: fromLists.State;
21 | feeds: fromFeeds.State;
22 | router: RouterState;
23 | }
24 |
25 | // Export all the reducers
26 | export default compose(combineReducers)({
27 | userAuth: userAuth,
28 | user: user,
29 | userList: userList,
30 | lists: lists,
31 | feeds: feeds,
32 | router: routerReducer
33 | });
34 |
35 | /**
36 | * Get Login State returns UserAuth from the store
37 | * and depending on the persense of access_token
38 | * in userAuth the login status of user is defined.
39 | */
40 | export function getLoginState(){
41 | return (state$: Observable) => state$
42 | .select(state => state.userAuth)
43 | }
44 |
45 | export const getUserState = (appState: AppState) => appState.user;
46 |
47 | export const getListsState = (appState: AppState) => appState.lists;
48 | export const getListsEntities = createSelector(getListsState, fromLists.getEntities);
49 | export const getListsIds = createSelector(getListsState, fromLists.getIds);
50 | export const getLists = createSelector(getListsEntities, getListsIds, (lists, ids) => {
51 | return ids.map(id => lists[id]);
52 | });
53 |
54 | export const getUserListsState = (appState: AppState) => appState.userList;
55 | export const getUserListEntities = createSelector(getUserListsState, fromUserList.getEntities);
56 | export const getUserListIds = createSelector(getUserListsState, fromUserList.getIds);
57 | export const getUserList = createSelector(getUserListEntities, getUserListIds, (userLists, ids) => {
58 | return ids.map(id => userLists[id]);
59 | });
60 |
61 |
62 | export const getFeedsState = (appState: AppState) => appState.feeds;
63 | export const getFeedsIds = createSelector(getFeedsState, fromFeeds.getIds);
64 | export const getFeedsEntities = createSelector(getFeedsState, fromFeeds.getEntities);
65 | export const getAllFeeds = createSelector(getFeedsEntities, getFeedsIds, (feeds, ids) => {
66 | return ids.map(id => feeds[id]);
67 | });
68 | export const getSelectedUserListID = createSelector(getFeedsState, fromFeeds.getSelectedUserListId);
69 |
70 | export const getSelectedUserListIdFeeds = createSelector(getAllFeeds, getSelectedUserListID, (feeds, userListId) => {
71 | return feeds.filter(feed => feed.user_list_id === userListId);
72 | })
73 |
74 |
75 | export const getSelectedFeedId = createSelector(getFeedsState, fromFeeds.getSelectedFeedId);
76 |
77 | export const getSelectedFeed = createSelector(getFeedsEntities, getSelectedFeedId, (feeds, id) => {
78 | return feeds[id];
79 | });
80 |
81 | export function isFollowing(listId){
82 | return createSelector(getUserListEntities, (userList) => {
83 | for (var key in userList) {
84 | var value = userList[key];
85 | if(value.list_id.$oid === listId){
86 | return true
87 | } else{
88 | console.log("list id is", this.list.id);
89 | return false
90 | }
91 | }
92 | })
93 | }
94 |
--------------------------------------------------------------------------------
/src/app/reducers/list.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action } from '@ngrx/store';
2 | import { List } from '../models/';
3 | import { ActionTypes } from '../actions/list.actions';
4 |
5 | export type State = {
6 | ids: string[];
7 | entities: { [id: string]: List };
8 | }
9 |
10 | const initialState: State = {
11 | ids: [],
12 | entities: {}
13 | }
14 |
15 | export default function(state = initialState, action: Action): State {
16 | switch(action.type){
17 | case ActionTypes.UPDATE_LISTS:
18 | case ActionTypes.RETRIVE_LISTS_SUCCESS: {
19 | const Lists: List[] = action.payload;
20 |
21 | const newLists: List[] = Lists;
22 |
23 | const newListIds = Lists
24 | .filter(list => !state.entities[list.id])
25 | .map(list => list.id);
26 |
27 | const newEntities = newLists
28 | .reduce((entities: { [id: string]: List }, list: List) => {
29 | return Object.assign(entities, {
30 | [list.id]: list
31 | });
32 | }, {});
33 |
34 | return Object.assign({} , state, {
35 | ids: [ ...state.ids, ...newListIds ],
36 | entities: Object.assign({}, state.entities, newEntities)
37 | })
38 | }
39 | case ActionTypes.UNFOLLOW_LIST_SUCCESS: {
40 | const unfollowed_list: List = action.payload.unfollowed_list;
41 |
42 | return Object.assign({}, state, {
43 | entities: Object.assign({}, state.entities,
44 | {[unfollowed_list.id]: unfollowed_list}
45 | )
46 | })
47 | }
48 | default: {
49 | return state;
50 | }
51 | }
52 | }
53 |
54 | export const getIds = (state: State) => state.ids;
55 | export const getEntities = (state: State) => state.entities;
56 |
--------------------------------------------------------------------------------
/src/app/reducers/user-auth.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action } from '@ngrx/store';
2 | import { ActionTypes, LoginActions } from '../actions/login.actions';
3 | import { UserAuth } from '../models/';
4 |
5 | export type AuthState = UserAuth;
6 |
7 | const initialState: AuthState = new UserAuth()
8 |
9 | export default function(state = initialState, action: Action): AuthState {
10 | switch(action.type){
11 |
12 | case ActionTypes.LOGIN_SUCCESS: {
13 | var userAuth: UserAuth = action.payload;
14 | return Object.assign({}, state, userAuth);
15 | }
16 |
17 | case ActionTypes.LOGIN_SERVER_SUCCESS: {
18 | var userAuth: UserAuth = action.payload;
19 | return Object.assign({}, state, userAuth);
20 | }
21 |
22 | case ActionTypes.SIGNUP_SUCCESS: {
23 | var userAuth: UserAuth = action.payload;
24 | return Object.assign({}, state, userAuth);
25 | }
26 |
27 | case ActionTypes.LOGOUT_SUCCESS: {
28 | return Object.assign({}, state, new UserAuth());
29 | }
30 |
31 | case ActionTypes.STORE_USER_SUCCESS: {
32 | var userAuth: UserAuth = action.payload
33 | return Object.assign({}, state, userAuth)
34 | }
35 |
36 | default: {
37 | return state;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/app/reducers/user-list.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action } from '@ngrx/store';
2 | import { UserList } from '../models/';
3 | import { ActionTypes as ListActionTypes }from '../actions/list.actions';
4 | import { ActionTypes as UserListActions } from '../actions/user-list.actions';
5 | import { ResponseParseService } from '../services/response-parse.service';
6 |
7 | export type State = {
8 | ids: string[];
9 | entities: { [id: string]: UserList };
10 | }
11 |
12 | const initialState: State = {
13 | ids: [],
14 | entities: {}
15 | };
16 |
17 | export default function(state = initialState, action: Action): State {
18 | switch(action.type){
19 | case ListActionTypes.FOLLOW_LIST_SUCCESS: {
20 | let response = action.payload;
21 |
22 | //Parse the response here using responseParserService
23 | let responseParserService = new ResponseParseService;
24 | let userList = responseParserService.createUserListobj(response.new_user_list);
25 |
26 | return Object.assign({}, state, {
27 | entities: Object.assign({}, state.entities,
28 | {[userList.id]: userList}
29 | ),
30 | ids: [ ...state.ids, userList.id ]
31 | })
32 | }
33 | case UserListActions.GET_USER_LISTS_SUCCESS: {
34 |
35 | const UserLists: UserList[] = action.payload;
36 | const newUserLists: UserList[] = UserLists
37 | .filter(list => !state.entities[list.id])
38 |
39 | const newUserListIds = UserLists
40 | .filter(userList => !state.entities[userList.id])
41 | .map(list => list.id);
42 |
43 | const newEntities = newUserLists
44 | .reduce((entities: { [id: string]: UserList }, userList: UserList) => {
45 | return Object.assign(entities, {[userList.id]: userList});
46 | }, {})
47 |
48 | return Object.assign({}, state, {
49 | ids: [...state.ids, ...newUserListIds],
50 | entities: Object.assign({}, state.entities, newEntities)
51 | })
52 | }
53 | case ListActionTypes.UNFOLLOW_LIST_SUCCESS: {
54 | const userListId: string = action.payload.user_list_id;
55 | const newIds = state.ids.filter(val => val != userListId);
56 | const newEntities = newIds.reduce((entities: { [id: string]: UserList }, id: string) => {
57 | return Object.assign(entities, {
58 | [id]: state.entities[id]
59 | });
60 | }, {});
61 |
62 | return Object.assign({}, state, {
63 | entities: newEntities,
64 | ids: newIds
65 | })
66 | }
67 |
68 | default: {
69 | return state;
70 | }
71 | }
72 | }
73 |
74 | export const getIds = (state: State) => state.ids;
75 | export const getEntities = (state: State) => state.entities;
--------------------------------------------------------------------------------
/src/app/reducers/user.reducer.ts:
--------------------------------------------------------------------------------
1 | import { Action } from '@ngrx/store';
2 | import { User } from '../models/';
3 | import { ActionTypes } from '../actions/user.actions';
4 | export type UserState = User;
5 |
6 | const initialState: UserState = new User();
7 |
8 | export default function(state = initialState, action: Action): UserState {
9 | switch(action.type){
10 | case ActionTypes.LOAD_PROFILE_SUCCESS: {
11 | const user: User = action.payload;
12 | return Object.assign({}, state, user)
13 | }
14 | default: {
15 | return state;
16 | }
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/src/app/services/api.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs/Observable';
3 | import { Http, Headers } from '@angular/http';
4 | import { environment } from '../../environments/environment';
5 | import { Restangular } from 'ng2-restangular';
6 | import { List, UserList, Tweet, User } from '../models';
7 |
8 | var BASE_URL: string = environment.baseUrl;
9 |
10 | @Injectable()
11 | export class ApiService {
12 |
13 | constructor(private http: Http, public restAngular: Restangular) { }
14 |
15 | retriveSuggestion(): Observable {
16 | var token = this.getServerToken()
17 | return this.restAngular.all('lists/suggest')
18 | .post(null, {}, { 'Authorization': token })
19 | }
20 |
21 | followList(listId: string): Observable {
22 | var token = this.getServerToken()
23 | var ListId = { id: listId }
24 | return this.restAngular.all('users/create_list')
25 | .post(ListId, {}, { 'Authorization': token })
26 | }
27 |
28 | getServerToken(): any {
29 | return localStorage.getItem('server_token');
30 | }
31 |
32 | getListsTimeLine(indexId: any): Observable {
33 | var token = this.getServerToken()
34 | var attr = { index_id: indexId };
35 | return this.restAngular
36 | .all('users/list_timeline')
37 | .post(attr, {}, { 'Authorization': token })
38 | }
39 |
40 | getUserLists(): Observable {
41 | var token = this.getServerToken()
42 | return this.restAngular
43 | .all('users/user_list')
44 | .post(null, {}, {'Authorization': token});
45 | }
46 |
47 | getUserDetail(): Observable{
48 | var token = this.getServerToken()
49 | return this.restAngular.all('users/user_detail')
50 | .post(null, {}, {'Authorization': token});
51 | }
52 |
53 | all_feeds(): any {
54 | var token = this.getServerToken()
55 | return this.restAngular.all('users/all_feeds')
56 | .post(null, {}, {'Authorization': token});
57 | }
58 |
59 |
60 | unFollowList(listId: string): Observable{
61 | var token = this.getServerToken()
62 | var ListId = { id: listId }
63 | return this.restAngular.all('users/unfollow_list')
64 | .post(ListId, {}, {'Authorization': token});
65 | }
66 |
67 | addToFav(feed: any): any {
68 | var token = this.getServerToken()
69 | var favoritedFeed = { feed: feed }
70 | return this.restAngular.all('tweets/add_tweet_to_fav')
71 | .post(favoritedFeed, {}, {'Authorization': token});
72 | }
73 |
74 | removeFromFav(feed: any): any {
75 | var token = this.getServerToken()
76 | var favoritedFeed = { feed: feed }
77 | return this.restAngular.all('tweets/remove_tweet_from_fav')
78 | .post(favoritedFeed, {}, {'Authorization': token});
79 | }
80 |
81 | retweet(feed: any): any {
82 | var token = this.getServerToken()
83 | var favoritedFeed = { feed: feed }
84 | return this.restAngular.all('tweets/retweet')
85 | .post(favoritedFeed, {}, {'Authorization': token});
86 | }
87 |
88 | reply(messageWithFeed: any): any {
89 | var token = this.getServerToken()
90 | return this.restAngular.all('tweets/reply')
91 | .post(messageWithFeed, {}, {'Authorization': token});
92 | }
93 | }
94 |
95 |
--------------------------------------------------------------------------------
/src/app/services/auth-guard.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Router, CanLoad, CanActivateChild, Route, ActivatedRouteSnapshot, CanActivate } from '@angular/router';
3 | import { Observable } from 'rxjs/Rx';
4 | import { Store } from '@ngrx/store';
5 | import { AppState, getLoginState } from '../reducers';
6 | import 'rxjs/add/operator/take';
7 | import 'rxjs/add/operator/filter';
8 |
9 |
10 | /**
11 | * Here we Override the CanActivate, CanLoad, CanActivateChild class methods
12 | * and use `guard` to return a boolean true or false depending on user login status.
13 | */
14 | @Injectable()
15 | export class AuthGuardService implements CanActivate, CanLoad, CanActivateChild {
16 |
17 | constructor(private router: Router,
18 | private store: Store) { }
19 |
20 | canLoad(route: Route): Observable{
21 | return this.guard();
22 | }
23 |
24 | canActivateChild(childRoute: ActivatedRouteSnapshot): Observable {
25 | return this.guard();
26 | }
27 |
28 | canActivate(){
29 | return this.guard();
30 | }
31 |
32 | /**
33 | * `Take` 1 takes the only 1 value from the stream of events
34 | * and stop taking the furthur values. i.e is it just stops
35 | * listening to the observable.
36 | * Ref. https://jsbin.com/xizixax/4/edit?html,js,console,output
37 | */
38 | guard(): Observable {
39 | return this.store.let(getLoginState())
40 | .take(1)
41 | .map(state => state.access_token ? true: this.handleAuthFail())
42 | }
43 |
44 | handleAuthFail(){
45 | this.router.navigate(['/login']);
46 | return false;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/services/response-parse.service.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs/Rx';
2 | import { Injectable } from '@angular/core';
3 | import { List, UserList, Tweet, User, Entities, Urls } from '../models';
4 |
5 | @Injectable()
6 | export class ResponseParseService {
7 |
8 | constructor() { }
9 |
10 | createUserListobj(dbUserListobj: any): UserList{
11 | var attr = {id: dbUserListobj.id,
12 | list_id: dbUserListobj.list_id,
13 | twitter_list_id: dbUserListobj.twitter_list_id,
14 | slug: dbUserListobj.slug,
15 | name: dbUserListobj.name
16 | }
17 | var userList: UserList = new UserList(attr);
18 | return userList;
19 | }
20 |
21 | createUserListsobj(dbUserListsobj: any): UserList[] {
22 | var userLists = [];
23 | dbUserListsobj.forEach(element => {
24 | var attr = {id: element.id,
25 | list_id: element.list_id,
26 | twitter_list_id: element.twitter_list_id,
27 | slug: element.slug,
28 | name: element.name
29 | }
30 | var userList = new UserList(attr);
31 | userLists.push(userList);
32 | });
33 | return userLists;
34 | }
35 |
36 | createSuggestedListsObj(response: any): List[] {
37 | var suggLists = []
38 | response.forEach(element => {
39 | var attr = {id: element.id, name: element.name,
40 | description: element.description,
41 | image_url: element.image_url,
42 | isFollowing: element.isFollowing
43 | }
44 | var suggestedList = new List(attr)
45 | suggLists.push(suggestedList);
46 | });
47 | return suggLists;
48 | }
49 |
50 |
51 | createTweetsObj(dbTweetsObj: any): any {
52 | return dbTweetsObj;
53 | /**
54 | * Below Code will be used in future to parse the response
55 | * Currently returning entire response object as it is.
56 | */
57 |
58 | // var tweets = [];
59 | // dbTweetsObj.forEach(element => {
60 | // // User
61 | // var dbuser = element.user;
62 | // var user_attr = {
63 | // id: dbuser.id,
64 | // name: dbuser.name,
65 | // screen_name: dbuser.screen_name,
66 | // profile_image_url: dbuser.profile_image_url
67 | // }
68 |
69 | // // Entities
70 | // var entity = element.entities
71 |
72 | // var entity_attr = {
73 | // hashtags: >entity.hashtags,
74 | // symbols: >entity.symbols,
75 | // urls: this.retriveUrlObj(entity.urls)
76 | // }
77 |
78 | // // Main Tweet
79 | // var attr = {
80 | // id: element.id,
81 | // text: element.text,
82 | // retweet_count: element.retweet_count,
83 | // favorite_count: element.favorite_count,
84 | // user: new User(user_attr),
85 | // user_list_id: element.user_list_id,
86 | // entities: new Entities(entity_attr)
87 | // }
88 |
89 | // var tweet = new Tweet(attr)
90 | // tweets.push(tweet);
91 | // });
92 | // return tweets;
93 | }
94 |
95 | retriveUrlObj(urls: Array): Urls[] {
96 | var urlsObj = [];
97 | urls.forEach(el => {
98 | var url_attr = {
99 | url: el.url,
100 | expanded_url: el.expanded_url,
101 | display_url: el.display_url,
102 | indices: el.indices
103 | }
104 |
105 | var url = new Urls(url_attr)
106 |
107 | urlsObj.push(url)
108 | }
109 | )
110 | return urlsObj;
111 | }
112 |
113 | createUserObj(response: any): User {
114 | var user_detail = response.user_detail
115 | var attr = {
116 | id: user_detail.id,
117 | name: user_detail.name,
118 | screen_name: user_detail.screen_name,
119 | profile_image_url: user_detail.profile_image_url_https,
120 | description: user_detail.description,
121 | location: user_detail.location
122 | }
123 | var user: User = new User(attr);
124 | return user;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/app/services/user-auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs/Observable';
3 | import { UserAuth, UserProfile, User } from '../models';
4 | import * as firebase from "firebase";
5 | import { Http, Headers } from '@angular/http';
6 | import { Secrets } from '../../secrets';
7 | import { environment } from '../../environments/environment';
8 | import { Restangular } from 'ng2-restangular';
9 |
10 | var BASE_URL: string = environment.baseUrl;
11 | /**
12 | * @class UserAuthService:
13 | *
14 | * Auth Service
15 | * Used to Login, Logout, Retrive User details and etc..
16 | * operation that can be carried out on provider API.
17 | */
18 | @Injectable()
19 | export class UserAuthService {
20 |
21 | /**
22 | * @constructor
23 | */
24 | constructor(private http: Http, public restAngular: Restangular) {
25 | var config = {
26 | apiKey: Secrets.firebaseApiKey,
27 | authDomain: Secrets.firebaseAuthDomain,
28 | databaseURL: Secrets.firebaseDatabaseUrl,
29 | storageBucket: Secrets.firebaseStorageBucket,
30 | messagingSenderId: Secrets.firebaseMessagingSenderId,
31 | };
32 | firebase.initializeApp(config);
33 | }
34 |
35 | signUp(): Observable {
36 | var provider = new firebase.auth.TwitterAuthProvider();
37 | return Observable.create(observer => {
38 | firebase.auth().signInWithPopup(provider).then(result => {
39 | const userAuth = new UserAuth(
40 | result.user.providerData[0]['uid'],
41 | result.credential.accessToken,
42 | result.credential.secret)
43 |
44 | observer.next(userAuth);
45 | })
46 | });
47 | }
48 |
49 | loginServer(userAuth: UserAuth): Observable {
50 | return this.restAngular.all('/auth/sign_in')
51 | .post(userAuth)
52 | .map(response => {
53 | // If Success
54 | if(response.status){
55 | var token = response.token;
56 | var newUserAuth = new UserAuth(userAuth.user_id,
57 | userAuth.access_token,
58 | userAuth.secret_token,
59 | token)
60 | return newUserAuth;
61 | } else {
62 | return userAuth
63 | }
64 | }
65 | )
66 | }
67 |
68 | login(): Observable {
69 | var provider = new firebase.auth.TwitterAuthProvider();
70 | return Observable.create(observer => {
71 | firebase.auth().signInWithPopup(provider).then(result => {
72 | const userAuth = new UserAuth(
73 | result.user.providerData[0]['uid'],
74 | result.credential.accessToken,
75 | result.credential.secret)
76 | observer.next(userAuth);
77 | })
78 | });
79 | }
80 |
81 | logout(): Observable {
82 | return Observable.of(
83 | localStorage.removeItem('access_token'),
84 | localStorage.removeItem('server_token'),
85 | localStorage.removeItem('user_id'),
86 | localStorage.removeItem('secret_token'));
87 | }
88 |
89 | storeUsertoBackend(payload): Observable {
90 | return this.restAngular.all('/auth/sign_up').post(payload)
91 | .map(response => {
92 | var token = response.token
93 | var userAuth = payload.userAuth
94 | var newUserAuth = new UserAuth(userAuth.user_id, userAuth.access_token, userAuth.secret_token, token)
95 | return newUserAuth;
96 | })
97 | }
98 |
99 | storeServerToken(token): void {
100 | localStorage.setItem('server_token', token);
101 | }
102 |
103 | storeUserAuthInLocalstorage(userAuth): void {
104 | localStorage.setItem('user_id', userAuth.user_id);
105 | localStorage.setItem('access_token', userAuth.access_token);
106 | localStorage.setItem('secret_token', userAuth.secret_token);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/app/util.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This function coerces a string into a string literal type.
3 | * Using tagged union types in TypeScript 2.0, this enables
4 | * powerful typechecking of our reducers.
5 | *
6 | * Since every action label passes through this function it
7 | * is a good place to ensure all of our action labels
8 | * are unique.
9 | */
10 |
11 | let typeCache: { [label: string]: boolean } = {};
12 | export function type(label: T | ''): T {
13 | if (typeCache[label]) {
14 | throw new Error(`Action type "${label}" is not unqiue"`);
15 | }
16 |
17 | typeCache[label] = true;
18 |
19 | return label;
20 | }
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/assets/background.jpg
--------------------------------------------------------------------------------
/src/assets/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-size: cover;
3 | }
--------------------------------------------------------------------------------
/src/assets/poi.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/assets/poi.gif
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | baseUrl: 'https://listify-backend.herokuapp.com/api'
4 | };
5 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false,
8 | baseUrl: 'http://127.0.0.1:3000/api'
9 | // baseUrl: 'https://listify-backend.herokuapp.com/api'
10 |
11 | };
12 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aviabird/listify/dc88bc50f333ebcfe0e9f272f90929b19844a13f/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Listify
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 | import { environment } from './environments/environment';
6 | import { AppModule } from './app/';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | platformBrowserDynamic().bootstrapModule(AppModule);
13 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | // This file includes polyfills needed by Angular 2 and is loaded before
2 | // the app. You can add your own extra polyfills to this file.
3 | import 'core-js/es6/symbol';
4 | import 'core-js/es6/object';
5 | import 'core-js/es6/function';
6 | import 'core-js/es6/parse-int';
7 | import 'core-js/es6/parse-float';
8 | import 'core-js/es6/number';
9 | import 'core-js/es6/math';
10 | import 'core-js/es6/string';
11 | import 'core-js/es6/date';
12 | import 'core-js/es6/array';
13 | import 'core-js/es6/regexp';
14 | import 'core-js/es6/map';
15 | import 'core-js/es6/set';
16 | import 'core-js/es6/reflect';
17 |
18 | import 'core-js/es7/reflect';
19 | import 'zone.js/dist/zone';
20 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | import './polyfills.ts';
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 |
10 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
11 | declare var __karma__: any;
12 | declare var require: any;
13 |
14 | // Prevent Karma from running prematurely.
15 | __karma__.loaded = function () {};
16 |
17 |
18 | Promise.all([
19 | System.import('@angular/core/testing'),
20 | System.import('@angular/platform-browser-dynamic/testing')
21 | ])
22 | // First, initialize the Angular testing environment.
23 | .then(([testing, testingBrowser]) => {
24 | testing.getTestBed().initTestEnvironment(
25 | testingBrowser.BrowserDynamicTestingModule,
26 | testingBrowser.platformBrowserDynamicTesting()
27 | );
28 | })
29 | // Then we find all the tests.
30 | .then(() => require.context('./', true, /\.spec\.ts/))
31 | // And load the modules.
32 | .then(context => context.keys().map(context))
33 | // Finally, start Karma to run the tests.
34 | .then(__karma__.start, __karma__.error);
35 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "emitDecoratorMetadata": true,
5 | "experimentalDecorators": true,
6 | "lib": ["es6", "dom"],
7 | "mapRoot": "./",
8 | "module": "es6",
9 | "moduleResolution": "node",
10 | "outDir": "../dist/out-tsc",
11 | "sourceMap": true,
12 | "target": "es5",
13 | "typeRoots": [
14 | "../node_modules/@types"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | // Typings reference file, you can add your own global typings here
2 | // https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html
3 |
4 | declare var System: any;
5 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "class-name": true,
7 | "comment-format": [
8 | true,
9 | "check-space"
10 | ],
11 | "curly": true,
12 | "eofline": true,
13 | "forin": true,
14 | "indent": [
15 | true,
16 | "spaces"
17 | ],
18 | "label-position": true,
19 | "label-undefined": true,
20 | "max-line-length": [
21 | true,
22 | 140
23 | ],
24 | "member-access": false,
25 | "member-ordering": [
26 | true,
27 | "static-before-instance",
28 | "variables-before-functions"
29 | ],
30 | "no-arg": true,
31 | "no-bitwise": true,
32 | "no-console": [
33 | true,
34 | "debug",
35 | "info",
36 | "time",
37 | "timeEnd",
38 | "trace"
39 | ],
40 | "no-construct": true,
41 | "no-debugger": true,
42 | "no-duplicate-key": true,
43 | "no-duplicate-variable": true,
44 | "no-empty": false,
45 | "no-eval": true,
46 | "no-inferrable-types": true,
47 | "no-shadowed-variable": true,
48 | "no-string-literal": false,
49 | "no-switch-case-fall-through": true,
50 | "no-trailing-whitespace": true,
51 | "no-unused-expression": true,
52 | "no-unused-variable": true,
53 | "no-unreachable": true,
54 | "no-use-before-declare": true,
55 | "no-var-keyword": true,
56 | "object-literal-sort-keys": false,
57 | "one-line": [
58 | true,
59 | "check-open-brace",
60 | "check-catch",
61 | "check-else",
62 | "check-whitespace"
63 | ],
64 | "quotemark": [
65 | true,
66 | "single"
67 | ],
68 | "radix": true,
69 | "semicolon": [
70 | "always"
71 | ],
72 | "triple-equals": [
73 | true,
74 | "allow-null-check"
75 | ],
76 | "typedef-whitespace": [
77 | true,
78 | {
79 | "call-signature": "nospace",
80 | "index-signature": "nospace",
81 | "parameter": "nospace",
82 | "property-declaration": "nospace",
83 | "variable-declaration": "nospace"
84 | }
85 | ],
86 | "variable-name": false,
87 | "whitespace": [
88 | true,
89 | "check-branch",
90 | "check-decl",
91 | "check-operator",
92 | "check-separator",
93 | "check-type"
94 | ],
95 |
96 | "directive-selector-prefix": [true, "ist"],
97 | "component-selector-prefix": [true, "ist"],
98 | "directive-selector-name": [true, "camelCase"],
99 | "component-selector-name": [true, "kebab-case"],
100 | "directive-selector-type": [true, "attribute"],
101 | "component-selector-type": [true, "element"],
102 | "use-input-property-decorator": true,
103 | "use-output-property-decorator": true,
104 | "use-host-property-decorator": true,
105 | "no-input-rename": true,
106 | "no-output-rename": true,
107 | "use-life-cycle-interface": true,
108 | "use-pipe-transform-interface": true,
109 | "component-class-suffix": true,
110 | "directive-class-suffix": true,
111 | "templates-use-public": true,
112 | "invoke-injectable": true
113 | }
114 | }
115 |
--------------------------------------------------------------------------------