Refactor stores to use generic store (CC-61)

pull/46/head
HardiReady 2018-10-07 17:46:29 +02:00
parent 421d15d19a
commit 8e46b6c6f1
19 changed files with 35 additions and 329 deletions

View File

@ -1,7 +1,6 @@
import {NgModule} from '@angular/core';
import {AdminComponent} from './admin.component';
import {SharedModule} from '../shared.module';
import {AppUserStore} from '../services/stores/app-user.store';
import {AppUserService} from '../services/app-user-service/app-user.service';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
@ -9,7 +8,7 @@ import {RouterModule} from '@angular/router';
@NgModule({
declarations: [AdminComponent],
imports: [CommonModule, SharedModule, RouterModule.forChild([{path: '', component: AdminComponent}])],
providers: [AppUserStore, AppUserService]
providers: [AppUserService]
})
export class AdminModule {
}

View File

@ -5,10 +5,7 @@ import {AppComponent} from './app.component';
import {LoginService} from './services/app-user-service/login-service';
import {appRouting, routingComponents, routingProviders} from './app.routing';
import {SquadService} from './services/army-management/squad.service';
import {SquadStore} from './services/stores/squad.store';
import {DecorationStore} from './services/stores/decoration.store';
import {DecorationService} from './services/army-management/decoration.service';
import {RankStore} from './services/stores/rank.store';
import {RankService} from './services/army-management/rank.service';
import {AppConfig} from './app.config';
import {LoginGuardAdmin, LoginGuardHL, LoginGuardSQL} from './login';
@ -19,7 +16,6 @@ import {PromotionService} from './services/army-management/promotion.service';
import {SharedModule} from './shared.module';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {UserService} from './services/army-management/user.service';
import {UserStore} from './services/stores/user.store';
import {CookieService} from 'ngx-cookie-service';
import {SnackBarService} from './services/user-interface/snack-bar/snack-bar.service';
import {HttpClientModule} from '@angular/common/http';
@ -48,14 +44,10 @@ import {SettingsService} from './services/settings.service';
LoginGuardHL,
LoginGuardAdmin,
UserService,
UserStore,
ArmyService,
SquadService,
SquadStore,
DecorationService,
DecorationStore,
RankService,
RankStore,
AwardingService,
PromotionService,
AppConfig,

View File

@ -1,18 +1,11 @@
import {NgModule} from '@angular/core';
import {SharedModule} from '../shared.module';
import {CommonModule} from '@angular/common';
import {RankService} from '../services/army-management/rank.service';
import {DecorationService} from '../services/army-management/decoration.service';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpClient} from '@angular/common/http';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {manageRouterModule, manageRoutingComponents} from './manage.routing';
import {InfiniteScrollModule} from 'ngx-infinite-scroll';
import {DecorationStore} from '../services/stores/decoration.store';
import {RankStore} from '../services/stores/rank.store';
import {SquadStore} from '../services/stores/squad.store';
import {SquadService} from '../services/army-management/squad.service';
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/manage/', '.json');
@ -35,15 +28,6 @@ export function createTranslateLoader(http: HttpClient) {
},
isolate: true
})
],
providers: [
RankStore,
RankService,
DecorationStore,
DecorationService,
SquadStore,
SquadService
]
})
export class ManageModule {

View File

@ -5,7 +5,7 @@ import {ActivatedRoute, Router} from '@angular/router';
import {Observable} from 'rxjs/Observable';
import {UserService} from '../../../services/army-management/user.service';
import {User} from '../../../models/model-interfaces';
import {ADD, LOAD} from '../../../services/stores/user.store';
import {ADD, LOAD} from '../../../services/stores/generic-store';
import {Fraction} from '../../../utils/fraction.enum';
import {MatButtonToggleGroup} from '@angular/material';
import {UIHelpers} from '../../../utils/global.helpers';

View File

@ -1,20 +1,20 @@
import {Injectable} from '@angular/core';
import {AppUser} from '../../models/model-interfaces';
import {Observable} from 'rxjs/Observable';
import {EDIT, LOAD, REMOVE} from '../stores/user.store';
import {EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client';
import {AppUserStore} from '../stores/app-user.store';
@Injectable()
export class AppUserService {
users$: Observable<AppUser[]>;
private appUserStore = new Store<AppUser>();
constructor(private http: HttpClient,
private appUserStore: AppUserStore,
private config: AppConfig) {
this.users$ = appUserStore.items$;
this.users$ = this.appUserStore.items$;
}
getUsers() {
@ -43,5 +43,4 @@ export class AppUserService {
this.appUserStore.dispatch({type: REMOVE, data: user});
});
}
}

View File

@ -12,7 +12,7 @@ export class AwardingService {
private config: AppConfig) {
}
getAwardings(fraction = '' , userId = '', inProgress = false, squadId = '') {
getAwardings(fraction = '', userId = '', inProgress = false, squadId = '') {
const getUrl = this.config.apiAwardPath
.concat('?inProgress=').concat(inProgress.toString())
.concat('&fractFilter=').concat(fraction)

View File

@ -2,22 +2,21 @@ import {Injectable} from '@angular/core';
import {Decoration} from '../../models/model-interfaces';
import {RequestMethod, RequestOptions, URLSearchParams} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {ADD, DecorationStore, EDIT, LOAD, REMOVE} from '../stores/decoration.store';
import {ADD, EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client';
@Injectable()
export class DecorationService {
decorations$: Observable<Decoration[]>;
constructor(private http: HttpClient,
private decorationStore: DecorationStore,
private config: AppConfig) {
this.decorations$ = decorationStore.items$;
}
decorationStore = new Store<Decoration>();
constructor(private http: HttpClient,
private config: AppConfig) {
this.decorations$ = this.decorationStore.items$;
}
findDecorations(query = '', fractionFilter?) {
const searchParams = new URLSearchParams();
@ -72,7 +71,6 @@ export class DecorationService {
body = decoration;
}
const options = new RequestOptions({
body: body,
method: requestMethod,
@ -86,7 +84,6 @@ export class DecorationService {
});
}
deleteDecoration(decoration: Decoration) {
return this.http.delete(this.config.apiDecorationPath + decoration._id)
.do(res => {
@ -94,4 +91,3 @@ export class DecorationService {
});
}
}

View File

@ -1,24 +1,22 @@
import {Injectable} from '@angular/core';
import {Decoration, Rank} from '../../models/model-interfaces';
import {Rank} from '../../models/model-interfaces';
import {RequestMethod, RequestOptions, URLSearchParams} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {LOAD} from '../stores/decoration.store';
import {ADD, EDIT, RankStore, REMOVE} from '../stores/rank.store';
import {ADD, EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client';
@Injectable()
export class RankService {
ranks$: Observable<Rank[]>;
constructor(private http: HttpClient,
private rankStore: RankStore,
private config: AppConfig) {
this.ranks$ = rankStore.items$;
}
rankStore = new Store<Rank>();
constructor(private http: HttpClient,
private config: AppConfig) {
this.ranks$ = this.rankStore.items$;
}
findRanks(query = '', fractionFilter?) {
const searchParams = new URLSearchParams();
@ -90,55 +88,10 @@ export class RankService {
});
}
/**
* send PATCH request to update db entry
*
* @param rank - Rank object with data to update, must contain _id
* @returns {Observable<T>}
*/
updateRank(rank: Rank) {
const options = new RequestOptions({
body: rank,
method: RequestMethod.Patch
});
return this.http.request(this.config.apiRankPath + rank._id, options)
.map(res => res.json())
.do(savedRank => {
const action = {type: EDIT, data: savedRank};
this.rankStore.dispatch(action);
});
}
/**
* sends PATCH with multiform data to
* update rank graphic with newly provided file
*
* @param rankID - id of rank to be updated
* @param imageFile - new image file to upload
*/
updateRankGraphic(rankId: string, imageFile: File) {
const formData: FormData = new FormData();
formData.append('_id', rankId);
formData.append('image', imageFile, imageFile.name);
// provide token as query value, because there is no actual body
// and x-access-token is ignored in multipart request
return this.http.patch(this.config.apiRankPath + rankId, formData)
.map(res => res.json())
.do(savedDecoration => {
const action = {type: EDIT, data: savedDecoration};
this.rankStore.dispatch(action);
});
}
deleteRank(rank: Rank) {
return this.http.delete(this.config.apiRankPath + rank._id)
.do(res => {
this.rankStore.dispatch({type: REMOVE, data: rank});
});
}
}

View File

@ -2,8 +2,7 @@ import {Injectable} from '@angular/core';
import {Squad} from '../../models/model-interfaces';
import {RequestMethod, RequestOptions, URLSearchParams} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {ADD, EDIT, LOAD, REMOVE, SquadStore} from '../stores/squad.store';
import {ADD, EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client';
@ -12,10 +11,11 @@ export class SquadService {
squads$: Observable<Squad[]>;
squadStore = new Store<Squad>();
constructor(private http: HttpClient,
private squadStore: SquadStore,
private config: AppConfig) {
this.squads$ = squadStore.items$;
this.squads$ = this.squadStore.items$;
}
findSquads(query = '', fractionFilter = '') {
@ -33,13 +33,11 @@ export class SquadService {
return this.squads$;
}
getSquad(id: number | string): Observable<Squad> {
return this.http.get(this.config.apiSquadPath + id)
.map(res => res.json());
}
/**
* For creating new data with POST or
* update existing with patch PATCH
@ -90,6 +88,4 @@ export class SquadService {
this.squadStore.dispatch({type: REMOVE, data: squad});
});
}
}

View File

@ -2,7 +2,7 @@ import {Injectable} from '@angular/core';
import {User} from '../../models/model-interfaces';
import {URLSearchParams} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {ADD, EDIT, LOAD, REMOVE, UserStore} from '../stores/user.store';
import {ADD, EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client';
@ -11,12 +11,13 @@ export class UserService {
users$: Observable<User[]>;
userStore = new Store<User>();
totalCount = 0;
constructor(private http: HttpClient,
private userStore: UserStore,
private config: AppConfig) {
this.users$ = userStore.items$;
this.users$ = this.userStore.items$;
}
findUsers(filter, limit?, offset?, action = LOAD) {
@ -81,6 +82,4 @@ export class UserService {
this.userStore.dispatch({type: REMOVE, data: user});
});
}
}

View File

@ -14,5 +14,4 @@ export class ArmyService {
return this.http.get(this.config.apiOverviewPath)
.map(res => res.json());
}
}

View File

@ -25,7 +25,6 @@ export class HttpClient {
this.router.navigate(['/login']);
}
}
}
get(url, searchParams?) {
@ -73,5 +72,4 @@ export class HttpClient {
return this.patch(requestUrl, options.body);
}
}
}

View File

@ -42,4 +42,3 @@ export class CampaignService {
.map(res => res.json());
}
}

View File

@ -1,41 +0,0 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {AppUser} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class AppUserStore {
private appUsers: AppUser[] = [];
items$ = new BehaviorSubject<AppUser[]>([]);
dispatch(action) {
this.appUsers = this._reduce(this.appUsers, action);
this.items$.next(this.appUsers);
}
_reduce(users: AppUser[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
return [...users, action.data];
case EDIT:
return users.map(user => {
const editedUser = action.data;
if (user._id !== editedUser._id) {
return user;
}
return editedUser;
});
case REMOVE:
return users.filter(user => user._id !== action.data._id);
default:
return users;
}
}
}

View File

@ -1,41 +0,0 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Decoration} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class DecorationStore {
private decorations: Decoration[] = [];
items$ = new BehaviorSubject<Decoration[]>([]);
dispatch(action) {
this.decorations = this._reduce(this.decorations, action);
this.items$.next(this.decorations);
}
_reduce(decorations: Decoration[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
return [...decorations, action.data];
case EDIT:
return decorations.map(decoration => {
const editedDecoration = action.data;
if (decoration._id !== editedDecoration._id) {
return decoration;
}
return editedDecoration;
});
case REMOVE:
return decorations.filter(decoration => decoration._id !== action.data._id);
default:
return decorations;
}
}
}

View File

@ -9,7 +9,7 @@ export const REMOVE = 'REMOVE';
type Id = string | number;
interface Identifiable {
id?: Id;
_id?: Id;
}
export class Store<T extends Identifiable> {
@ -28,15 +28,15 @@ export class Store<T extends Identifiable> {
case ADD:
return [...items, action.data];
case EDIT:
return items.map(task => {
const editedTask = action.data;
if (task.id !== editedTask.id) {
return task;
return items.map(item => {
const editedItem = action.data;
if (item._id !== editedItem._id) {
return item;
}
return editedTask;
return editedItem;
});
case REMOVE:
return items.filter(task => task.id !== action.data.id);
return items.filter(item => item._id !== action.data._id);
default:
return items;
}

View File

@ -1,41 +0,0 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Rank} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class RankStore {
private ranks: Rank[] = [];
items$ = new BehaviorSubject<Rank[]>([]);
dispatch(action) {
this.ranks = this._reduce(this.ranks, action);
this.items$.next(this.ranks);
}
_reduce(ranks: Rank[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
return [...ranks, action.data];
case EDIT:
return ranks.map(decoration => {
const editedRank = action.data;
if (decoration._id !== editedRank._id) {
return decoration;
}
return editedRank;
});
case REMOVE:
return ranks.filter(decoration => decoration._id !== action.data._id);
default:
return ranks;
}
}
}

View File

@ -1,41 +0,0 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Squad} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class SquadStore {
private squads: Squad[] = [];
items$ = new BehaviorSubject<Squad[]>([]);
dispatch(action) {
this.squads = this._reduce(this.squads, action);
this.items$.next(this.squads);
}
_reduce(squads: Squad[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
return [...squads, action.data];
case EDIT:
return squads.map(squad => {
const editedSquad = action.data;
if (squad._id !== editedSquad._id) {
return squad;
}
return editedSquad;
});
case REMOVE:
return squads.filter(squad => squad._id !== action.data._id);
default:
return squads;
}
}
}

View File

@ -1,44 +0,0 @@
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {User} from '../../models/model-interfaces';
export const LOAD = 'LOAD';
export const ADD = 'ADD';
export const EDIT = 'EDIT';
export const REMOVE = 'REMOVE';
export class UserStore {
private users: User[] = [];
items$ = new BehaviorSubject<User[]>([]);
dispatch(action) {
this.users = this._reduce(this.users, action);
this.items$.next(this.users);
}
_reduce(users: User[], action) {
switch (action.type) {
case LOAD:
return [...action.data];
case ADD:
if (action.data instanceof Array) {
return users.concat(action.data);
}
return [...users, action.data];
case EDIT:
return users.map(user => {
const editedUser = action.data;
if (user._id !== editedUser._id) {
return user;
}
return editedUser;
});
case REMOVE:
return users.filter(user => user._id !== action.data._id);
default:
return users;
}
}
}