diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3d7001bfe..25e6958f1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,7 +8,12 @@ import { takeUntil } from 'rxjs/operators/takeUntil'; import { map } from 'rxjs/operators/map'; import { filter } from 'rxjs/operators/filter'; -import { login, logout, selectorAuth, routerTransition } from '@app/core'; +import { + ActionAuthLogin, + ActionAuthLogout, + selectorAuth, + routerTransition +} from '@app/core'; import { environment as env } from '@env/environment'; import { selectorSettings } from './settings'; @@ -82,10 +87,10 @@ export class AppComponent implements OnInit, OnDestroy { } onLoginClick() { - this.store.dispatch(login()); + this.store.dispatch(new ActionAuthLogin()); } onLogoutClick() { - this.store.dispatch(logout()); + this.store.dispatch(new ActionAuthLogout()); } } diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index d167f6093..cc1be08a6 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; +import { Action } from '@ngrx/store'; import { Actions, Effect } from '@ngrx/effects'; import { Observable } from 'rxjs/Observable'; import { tap } from 'rxjs/operators/tap'; import { LocalStorageService } from '../local-storage/local-storage.service'; -import { Action } from '../core.interfaces'; -import { AUTH_KEY, AUTH_LOGIN, AUTH_LOGOUT } from './auth.reducer'; +import { AUTH_KEY, AuthActionTypes } from './auth.reducer'; @Injectable() export class AuthEffects { @@ -18,7 +18,7 @@ export class AuthEffects { @Effect({ dispatch: false }) login(): Observable { return this.actions$ - .ofType(AUTH_LOGIN) + .ofType(AuthActionTypes.LOGIN) .pipe( tap(action => this.localStorageService.setItem(AUTH_KEY, { isAuthenticated: true }) @@ -29,7 +29,7 @@ export class AuthEffects { @Effect({ dispatch: false }) logout(): Observable { return this.actions$ - .ofType(AUTH_LOGOUT) + .ofType(AuthActionTypes.LOGOUT) .pipe( tap(action => this.localStorageService.setItem(AUTH_KEY, { isAuthenticated: false }) diff --git a/src/app/core/auth/auth.reducer.ts b/src/app/core/auth/auth.reducer.ts index 4033ad98f..69b6aea36 100644 --- a/src/app/core/auth/auth.reducer.ts +++ b/src/app/core/auth/auth.reducer.ts @@ -1,26 +1,36 @@ -import { Action } from '../core.interfaces'; +import { Action } from '@ngrx/store'; + +export const AUTH_KEY = 'AUTH'; + +export enum AuthActionTypes { + LOGIN = '[Auth] Login', + LOGOUT = '[Auth] Logout' +} + +export class ActionAuthLogin implements Action { + readonly type = AuthActionTypes.LOGIN; +} + +export class ActionAuthLogout implements Action { + readonly type = AuthActionTypes.LOGOUT; +} + +export type AuthActions = ActionAuthLogin | ActionAuthLogout; export const initialState = { isAuthenticated: false }; -export const AUTH_KEY = 'AUTH'; -export const AUTH_LOGIN = 'AUTH_LOGIN'; -export const AUTH_LOGOUT = 'AUTH_LOGOUT'; - -export const login = () => ({ type: AUTH_LOGIN }); -export const logout = () => ({ type: AUTH_LOGOUT }); - export const selectorAuth = state => state.auth; -export function authReducer(state = initialState, action: Action) { +export function authReducer(state = initialState, action: AuthActions) { switch (action.type) { - case AUTH_LOGIN: + case AuthActionTypes.LOGIN: return Object.assign({}, state, { isAuthenticated: true }); - case AUTH_LOGOUT: + case AuthActionTypes.LOGOUT: return Object.assign({}, state, { isAuthenticated: false }); diff --git a/src/app/core/core.interfaces.ts b/src/app/core/core.interfaces.ts deleted file mode 100644 index c3bf23067..000000000 --- a/src/app/core/core.interfaces.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Action { - type: string; - payload: any; -} diff --git a/src/app/core/index.ts b/src/app/core/index.ts index d53c2f449..ff460c874 100644 --- a/src/app/core/index.ts +++ b/src/app/core/index.ts @@ -1,5 +1,4 @@ export * from './local-storage/local-storage.service'; export * from './animations/router.transition'; export * from './auth/auth.reducer'; -export * from './core.interfaces'; export * from './core.module'; diff --git a/src/app/examples/stock-market/stock-market.component.ts b/src/app/examples/stock-market/stock-market.component.ts index 7fa57e7ea..8d87ec1ae 100644 --- a/src/app/examples/stock-market/stock-market.component.ts +++ b/src/app/examples/stock-market/stock-market.component.ts @@ -3,7 +3,10 @@ import { Store } from '@ngrx/store'; import { Subject } from 'rxjs/Subject'; import { takeUntil } from 'rxjs/operators/takeUntil'; -import { actionRetrieveStock, selectorStocks } from './stock-market.reducer'; +import { + ActionStockMarketRetrieve, + selectorStocks +} from './stock-market.reducer'; @Component({ selector: 'anms-stock-market', @@ -28,7 +31,9 @@ export class StockMarketComponent implements OnInit, OnDestroy { if (!this.initialized) { this.initialized = true; - this.store.dispatch(actionRetrieveStock(stocks.symbol)); + this.store.dispatch( + new ActionStockMarketRetrieve({ symbol: stocks.symbol }) + ); } }); } @@ -39,6 +44,6 @@ export class StockMarketComponent implements OnInit, OnDestroy { } onSymbolChange(symbol: string) { - this.store.dispatch(actionRetrieveStock(symbol)); + this.store.dispatch(new ActionStockMarketRetrieve({ symbol })); } } diff --git a/src/app/examples/stock-market/stock-market.effects.ts b/src/app/examples/stock-market/stock-market.effects.ts index 3c99052c0..2e27a1df3 100644 --- a/src/app/examples/stock-market/stock-market.effects.ts +++ b/src/app/examples/stock-market/stock-market.effects.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Action } from '@ngrx/store'; import { Actions, Effect } from '@ngrx/effects'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; @@ -9,13 +10,14 @@ import { distinctUntilChanged } from 'rxjs/operators/distinctUntilChanged'; import { switchMap } from 'rxjs/operators/switchMap'; import { catchError } from 'rxjs/operators/catchError'; -import { LocalStorageService, Action } from '@app/core'; +import { LocalStorageService } from '@app/core'; import { + ActionStockMarketRetrieve, + ActionStockMarketRetrieveError, + ActionStockMarketRetrieveSuccess, STOCK_MARKET_KEY, - STOCK_MARKET_RETRIEVE, - STOCK_MARKET_RETRIEVE_SUCCESS, - STOCK_MARKET_RETRIEVE_ERROR + StockMarketActionTypes } from './stock-market.reducer'; import { StockMarketService } from './stock-market.service'; @@ -29,24 +31,23 @@ export class StockMarketEffects { @Effect() retrieveStock(): Observable { - return this.actions$.ofType(STOCK_MARKET_RETRIEVE).pipe( - tap(action => + return this.actions$.ofType(StockMarketActionTypes.RETRIEVE).pipe( + tap((action: ActionStockMarketRetrieve) => this.localStorageService.setItem(STOCK_MARKET_KEY, { - symbol: action.payload + symbol: action.payload.symbol }) ), distinctUntilChanged(), debounceTime(500), - switchMap(action => - this.service.retrieveStock(action.payload).pipe( - map(stock => ({ - type: STOCK_MARKET_RETRIEVE_SUCCESS, - payload: stock - })), - catchError(err => - of({ type: STOCK_MARKET_RETRIEVE_ERROR, payload: err }) + switchMap((action: ActionStockMarketRetrieve) => + this.service + .retrieveStock(action.payload.symbol) + .pipe( + map(stock => new ActionStockMarketRetrieveSuccess({ stock })), + catchError(error => + of(new ActionStockMarketRetrieveError({ error })) + ) ) - ) ) ); } diff --git a/src/app/examples/stock-market/stock-market.reducer.ts b/src/app/examples/stock-market/stock-market.reducer.ts index 25b49a8ff..983ce4cf0 100644 --- a/src/app/examples/stock-market/stock-market.reducer.ts +++ b/src/app/examples/stock-market/stock-market.reducer.ts @@ -1,43 +1,65 @@ -import { Action } from '@app/core'; +import { Action } from '@ngrx/store'; +import { HttpErrorResponse } from '@angular/common/http'; + +export const STOCK_MARKET_KEY = 'EXAMPLES.STOCKS'; + +export enum StockMarketActionTypes { + RETRIEVE = '[Todos] Retrieve', + RETRIEVE_SUCCESS = '[Todos] Retrieve Success', + RETRIEVE_ERROR = '[Todos] Retrieve Error' +} + +export class ActionStockMarketRetrieve implements Action { + readonly type = StockMarketActionTypes.RETRIEVE; + constructor(public payload: { symbol: string }) {} +} + +export class ActionStockMarketRetrieveSuccess implements Action { + readonly type = StockMarketActionTypes.RETRIEVE_SUCCESS; + constructor(public payload: { stock: Stock }) {} +} + +export class ActionStockMarketRetrieveError implements Action { + readonly type = StockMarketActionTypes.RETRIEVE_ERROR; + constructor(public payload: { error: HttpErrorResponse }) {} +} + +export type StockMarketActions = + | ActionStockMarketRetrieve + | ActionStockMarketRetrieveSuccess + | ActionStockMarketRetrieveError; export const initialState = { symbol: 'GOOGL' }; -export const STOCK_MARKET_KEY = 'EXAMPLES.STOCKS'; -export const STOCK_MARKET_RETRIEVE = 'STOCK_MARKET_RETRIEVE'; -export const STOCK_MARKET_RETRIEVE_SUCCESS = 'STOCK_MARKET_RETRIEVE_SUCCESS'; -export const STOCK_MARKET_RETRIEVE_ERROR = 'STOCK_MARKET_RETRIEVE_ERROR'; - -export const actionRetrieveStock = (symbol: string) => ({ - type: STOCK_MARKET_RETRIEVE, - payload: symbol -}); - export const selectorStocks = state => state.examples.stocks; -export function stockMarketReducer(state = initialState, action: Action) { +export function stockMarketReducer( + state = initialState, + action: StockMarketActions +) { switch (action.type) { - case STOCK_MARKET_RETRIEVE: + case StockMarketActionTypes.RETRIEVE: return Object.assign({}, state, { loading: true, stock: null, error: null, - symbol: action.payload + symbol: action.payload.symbol }); - case STOCK_MARKET_RETRIEVE_SUCCESS: + case StockMarketActionTypes.RETRIEVE_SUCCESS: return Object.assign({}, state, { loading: false, - stock: action.payload, + stock: action.payload.stock, error: null }); - case STOCK_MARKET_RETRIEVE_ERROR: + case StockMarketActionTypes.RETRIEVE_ERROR: return Object.assign({}, state, { loading: false, stock: null, - error: action.payload + error: action.payload.error }); default: diff --git a/src/app/examples/todos/todos.component.ts b/src/app/examples/todos/todos.component.ts index d859a15b8..643544e28 100644 --- a/src/app/examples/todos/todos.component.ts +++ b/src/app/examples/todos/todos.component.ts @@ -6,14 +6,14 @@ import { takeUntil } from 'rxjs/operators/takeUntil'; import { ANIMATE_ON_ROUTE_ENTER } from '@app/core'; import { - actionAddTodo, - actionPersistTodos, - actionToggleTodo, - actionRemoveDoneTodos, - actionFilterTodos, + ActionTodosAdd, + ActionTodosPersist, + ActionTodosFilter, + ActionTodosRemoveDone, + ActionTodosToggle, selectorTodos, Todo, - TodoFilter + TodosFilter } from './todos.reducer'; @Component({ @@ -36,7 +36,7 @@ export class TodosComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribe$)) .subscribe(todos => { this.todos = todos; - this.store.dispatch(actionPersistTodos(todos)); + this.store.dispatch(new ActionTodosPersist({ todos })); }); } @@ -72,19 +72,19 @@ export class TodosComponent implements OnInit, OnDestroy { } onAddTodo() { - this.store.dispatch(actionAddTodo(this.newTodo)); + this.store.dispatch(new ActionTodosAdd({ name: this.newTodo })); this.newTodo = ''; } onToggleTodo(todo: Todo) { - this.store.dispatch(actionToggleTodo(todo.id)); + this.store.dispatch(new ActionTodosToggle({ id: todo.id })); } onRemoveDoneTodos() { - this.store.dispatch(actionRemoveDoneTodos()); + this.store.dispatch(new ActionTodosRemoveDone()); } - onFilterTodos(filter: TodoFilter) { - this.store.dispatch(actionFilterTodos(filter)); + onFilterTodos(filter: TodosFilter) { + this.store.dispatch(new ActionTodosFilter({ filter })); } } diff --git a/src/app/examples/todos/todos.effects.ts b/src/app/examples/todos/todos.effects.ts index dc69c5900..abecda6db 100644 --- a/src/app/examples/todos/todos.effects.ts +++ b/src/app/examples/todos/todos.effects.ts @@ -1,11 +1,16 @@ import { Injectable } from '@angular/core'; +import { Action } from '@ngrx/store'; import { Actions, Effect } from '@ngrx/effects'; import { Observable } from 'rxjs/Observable'; import { tap } from 'rxjs/operators/tap'; -import { LocalStorageService, Action } from '@app/core'; +import { LocalStorageService } from '@app/core'; -import { TODOS_KEY, TODOS_PERSIST } from './todos.reducer'; +import { + ActionTodosPersist, + TODOS_KEY, + TodosActionTypes +} from './todos.reducer'; @Injectable() export class TodosEffects { @@ -17,10 +22,10 @@ export class TodosEffects { @Effect({ dispatch: false }) persistTodos(): Observable { return this.actions$ - .ofType(TODOS_PERSIST) + .ofType(TodosActionTypes.PERSIST) .pipe( - tap(action => - this.localStorageService.setItem(TODOS_KEY, action.payload) + tap((action: ActionTodosPersist) => + this.localStorageService.setItem(TODOS_KEY, action.payload.todos) ) ); } diff --git a/src/app/examples/todos/todos.reducer.ts b/src/app/examples/todos/todos.reducer.ts index 81632bdac..45b04525b 100644 --- a/src/app/examples/todos/todos.reducer.ts +++ b/src/app/examples/todos/todos.reducer.ts @@ -1,6 +1,48 @@ import { v4 as uuid } from 'uuid'; +import { Action } from '@ngrx/store'; -import { Action } from '@app/core'; +export const TODOS_KEY = 'EXAMPLES.TODOS'; + +export enum TodosActionTypes { + ADD = '[Todos] Add', + TOGGLE = '[Todos] Toggle', + REMOVE_DONE = '[Todos] Remove Done', + FILTER = '[Todos] Filter', + PERSIST = '[Todos] Persist' +} + +export class ActionTodosAdd implements Action { + readonly type = TodosActionTypes.ADD; + constructor(public payload: { name: string }) {} +} + +export class ActionTodosToggle implements Action { + readonly type = TodosActionTypes.TOGGLE; + constructor(public payload: { id: string }) {} +} + +export class ActionTodosRemoveDone implements Action { + readonly type = TodosActionTypes.REMOVE_DONE; +} + +export class ActionTodosFilter implements Action { + readonly type = TodosActionTypes.FILTER; + constructor(public payload: { filter: TodosFilter }) {} +} + +export class ActionTodosPersist implements Action { + readonly type = TodosActionTypes.PERSIST; + constructor(public payload: { todos: Todo[] }) {} +} + +export type TodosActions = + | ActionTodosAdd + | ActionTodosToggle + | ActionTodosRemoveDone + | ActionTodosFilter + | ActionTodosPersist; + +export type TodosFilter = 'ALL' | 'DONE' | 'ACTIVE'; export const initialState = { items: [ @@ -15,49 +57,22 @@ export const initialState = { filter: 'ALL' }; -export type TodoFilter = 'ALL' | 'DONE' | 'ACTIVE'; - -export const TODOS_KEY = 'EXAMPLES.TODOS'; -export const TODOS_ADD = 'TODOS_ADD'; -export const TODOS_TOGGLE = 'TODOS_TOGGLE'; -export const TODOS_REMOVE_DONE = 'TODOS_REMOVE_DONE'; -export const TODOS_FILTER = 'TODOS_FILTER'; -export const TODOS_PERSIST = 'TODOS_PERSIST'; - -export const actionRemoveDoneTodos = () => ({ type: TODOS_REMOVE_DONE }); -export const actionAddTodo = (name: string) => ({ - type: TODOS_ADD, - payload: name -}); -export const actionToggleTodo = (id: string) => ({ - type: TODOS_TOGGLE, - payload: id -}); -export const actionPersistTodos = todos => ({ - type: TODOS_PERSIST, - payload: todos -}); -export const actionFilterTodos = (filter: TodoFilter) => ({ - type: TODOS_FILTER, - payload: filter -}); - export const selectorTodos = state => state.examples.todos; -export function todosReducer(state = initialState, action: Action) { +export function todosReducer(state = initialState, action: TodosActions) { switch (action.type) { - case TODOS_ADD: + case TodosActionTypes.ADD: return Object.assign({}, state, { items: state.items.concat({ id: uuid(), - name: action.payload, + name: action.payload.name, done: false }) }); - case TODOS_TOGGLE: + case TodosActionTypes.TOGGLE: state.items.some((item: Todo) => { - if (item.id === action.payload) { + if (item.id === action.payload.id) { item.done = !item.done; return true; } @@ -66,13 +81,13 @@ export function todosReducer(state = initialState, action: Action) { items: [...state.items] }); - case TODOS_REMOVE_DONE: + case TodosActionTypes.REMOVE_DONE: return Object.assign({}, state, { items: state.items.filter((item: Todo) => !item.done) }); - case TODOS_FILTER: - return Object.assign({}, state, { filter: action.payload }); + case TodosActionTypes.FILTER: + return Object.assign({}, state, { filter: action.payload.filter }); default: return state; diff --git a/src/app/settings/settings.effects.ts b/src/app/settings/settings.effects.ts index 761b294f2..514a209fc 100644 --- a/src/app/settings/settings.effects.ts +++ b/src/app/settings/settings.effects.ts @@ -1,11 +1,16 @@ import { Injectable } from '@angular/core'; +import { Action } from '@ngrx/store'; import { Actions, Effect } from '@ngrx/effects'; import { Observable } from 'rxjs/Observable'; import { tap } from 'rxjs/operators/tap'; -import { LocalStorageService, Action } from '@app/core'; +import { LocalStorageService } from '@app/core'; -import { SETTINGS_KEY, SETTINGS_CHANGE_THEME } from './settings.reducer'; +import { + SETTINGS_KEY, + SettingsActionTypes, + ActionSettingsChangeTheme +} from './settings.reducer'; @Injectable() export class SettingsEffects { @@ -16,10 +21,10 @@ export class SettingsEffects { @Effect({ dispatch: false }) persistThemeSettings(): Observable { - return this.actions$.ofType(SETTINGS_CHANGE_THEME).pipe( - tap(action => + return this.actions$.ofType(SettingsActionTypes.CHANGE_THEME).pipe( + tap((action: ActionSettingsChangeTheme) => this.localStorageService.setItem(SETTINGS_KEY, { - theme: action.payload + theme: action.payload.theme }) ) ); diff --git a/src/app/settings/settings.reducer.ts b/src/app/settings/settings.reducer.ts index 6f0983d14..f988e32e1 100644 --- a/src/app/settings/settings.reducer.ts +++ b/src/app/settings/settings.reducer.ts @@ -1,23 +1,28 @@ -import { Action } from '@app/core'; +import { Action } from '@ngrx/store'; + +export const SETTINGS_KEY = 'SETTINGS'; + +export enum SettingsActionTypes { + CHANGE_THEME = '[Settings] Change Theme' +} + +export class ActionSettingsChangeTheme implements Action { + readonly type = SettingsActionTypes.CHANGE_THEME; + constructor(public payload: { theme: string }) {} +} + +export type SettingsActions = ActionSettingsChangeTheme; export const initialState = { theme: 'DEFAULT-THEME' }; -export const SETTINGS_KEY = 'SETTINGS'; -export const SETTINGS_CHANGE_THEME = 'SETTINGS_CHANGE_THEME'; - -export const actionChangeTheme = (theme: string) => ({ - type: SETTINGS_CHANGE_THEME, - payload: theme -}); - export const selectorSettings = state => state.settings || { theme: '' }; -export function settingsReducer(state = initialState, action: Action) { +export function settingsReducer(state = initialState, action: SettingsActions) { switch (action.type) { - case SETTINGS_CHANGE_THEME: - return { theme: action.payload }; + case SettingsActionTypes.CHANGE_THEME: + return { theme: action.payload.theme }; default: return state; diff --git a/src/app/settings/settings/settings.component.ts b/src/app/settings/settings/settings.component.ts index 928b58690..7675db223 100644 --- a/src/app/settings/settings/settings.component.ts +++ b/src/app/settings/settings/settings.component.ts @@ -3,7 +3,10 @@ import { Store } from '@ngrx/store'; import { Subject } from 'rxjs/Subject'; import { takeUntil } from 'rxjs/operators/takeUntil'; -import { selectorSettings, actionChangeTheme } from '../settings.reducer'; +import { + selectorSettings, + ActionSettingsChangeTheme +} from '../settings.reducer'; @Component({ selector: 'anms-settings', @@ -34,7 +37,7 @@ export class SettingsComponent implements OnInit, OnDestroy { this.unsubscribe$.complete(); } - onThemeSelect({ value }) { - this.store.dispatch(actionChangeTheme(value)); + onThemeSelect({ value: theme }) { + this.store.dispatch(new ActionSettingsChangeTheme({ theme })); } }