Refactoring for landingpage support
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good

This commit is contained in:
Willem Dantuma 2020-10-29 19:14:06 +01:00
parent a5ece9b453
commit 46c5f74b49
8 changed files with 145 additions and 86 deletions

View File

@ -34,7 +34,7 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
@Input() selectedFeature: Feature; @Input() selectedFeature: Feature;
@Input() selectedItem: IItem; @Input() selectedItem: IItem;
@Input() styles:IStyles; @Input() styles:IStyles;
@Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>(); @Output() onFeatureSelected: EventEmitter<Feature> = new EventEmitter<Feature>();
@Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>(); @Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>();
private stylesCache:IStyles = {}; private stylesCache:IStyles = {};
@ -96,9 +96,9 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
this.map.instance.addInteraction(this._hoverSelect); this.map.instance.addInteraction(this._hoverSelect);
this._select.on('select', (e) => { this._select.on('select', (e) => {
if (e.selected.length > 0 && e.selected[0]) { if (e.selected.length > 0 && e.selected[0]) {
this.onFeaturesSelected.emit(e.selected[0]); this.onFeatureSelected.emit(e.selected[0]);
} else { } else {
this.onFeaturesSelected.emit(null); this.onFeatureSelected.emit(null);
} }
}); });
this._hoverSelect.on('select', (e) => { this._hoverSelect.on('select', (e) => {

View File

@ -35,7 +35,7 @@
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers> <fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
<fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers> <fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers>
<aol-layer-vector> <aol-layer-vector>
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector> <fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector> </aol-layer-vector>
<!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> --> <!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> -->
<div class="control-container"> <div class="control-container">

View File

@ -66,6 +66,8 @@ export const ONLINE = '[AppCommon] Online';
export const OFFLINE = '[AppCommon] Offline'; export const OFFLINE = '[AppCommon] Offline';
export const SETPAGEMODE = '[AppCommon] SetPageMode';
export class InitUser implements Action { export class InitUser implements Action {
readonly type = INITUSER; readonly type = INITUSER;
@ -299,6 +301,12 @@ export class Offline implements Action {
constructor() { } constructor() { }
} }
export class SetPageMode implements Action {
readonly type = SETPAGEMODE;
constructor(public pageMode:boolean) {}
}
export type Actions = OpenModal export type Actions = OpenModal
| InitRoot | InitRoot
@ -337,6 +345,7 @@ export type Actions = OpenModal
| ToggleAccountMenu | ToggleAccountMenu
| CloseAll | CloseAll
| Online | Online
| Offline; | Offline
| SetPageMode;

View File

@ -1,7 +1,14 @@
<div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async)}"> <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary"> <nav class="navbar navbar-light navbar-expand bg-light navigation-clean">
<button type="button" class="btn btn-outline-light" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button> <div class="header-logo pageonly"><router-outlet name="header-logo"></router-outlet></div>
<button type="button" class="btn btn-outline-secondary apponly" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button>
<router-outlet name="menu" class="ml-4"></router-outlet> <router-outlet name="menu" class="ml-4"></router-outlet>
<div class="collapse navbar-collapse pageonly">
<a class="btn btn-primary ml-auto" role="button" [routerLink]="['/map']">
<span *ngIf="(user|async)==null" i18n>Sign in</span>
<span *ngIf="(user|async)!=null" i18n>To app</span>
</a>
</div>
</nav> </nav>
<div class="body"> <div class="body">
<router-outlet></router-outlet> <router-outlet></router-outlet>
@ -11,7 +18,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="body"> <div class="body">
<div class="d-flex flex-row"> <div class="d-flex flex-row">
<div class="mt-2 mb-2 flex-grow-1 logo"><router-outlet name="side-panel-logo"></router-outlet></div> <div class="mt-2 mb-2 flex-grow-1 logo" (click)="handleHome($event)"><router-outlet name="side-panel-logo"></router-outlet></div>
<div class="mt-2 mb-2 ml-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fa fa-times" aria-hidden="true"></i></button></div> <div class="mt-2 mb-2 ml-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fa fa-times" aria-hidden="true"></i></button></div>
</div> </div>
<div class="d-flex flex-column cards"> <div class="d-flex flex-column cards">
@ -23,10 +30,10 @@
<ng-container *ngIf="showUploadProgress"> <ng-container *ngIf="showUploadProgress">
<fm-resumable-file-upload></fm-resumable-file-upload> <fm-resumable-file-upload></fm-resumable-file-upload>
</ng-container> </ng-container>
<div class="user-menu"> <div class="user-menu apponly">
<fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu> <fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu>
</div> </div>
<div class="healthstatus-container online" [ngClass]="{'online' :(isOnline|async)}"> <div class="healthstatus-container online apponly" [ngClass]="{'online' :(isOnline|async)}">
<div class="healthstatus alert alert-danger m-0" > <div class="healthstatus alert alert-danger m-0" >
<span i18n>Not connected, make sure your device has an active internet connection</span> <span i18n>Not connected, make sure your device has an active internet connection</span>
</div> </div>

View File

@ -3,6 +3,7 @@
@import "~bootstrap/scss/bootstrap.scss"; @import "~bootstrap/scss/bootstrap.scss";
// custom styles // custom styles
.btn:focus { .btn:focus {
@ -25,7 +26,7 @@
*/ */
body { background: #f1f1f1; line-height: 18px; user-select:none;} body { background: #f1f1f1; line-height: 18px; user-select:none;font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif;}
.navbar-brand { .navbar-brand {
padding-top: .5rem; padding-top: .5rem;
@ -56,6 +57,19 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;}
overflow: hidden; overflow: hidden;
} }
.app.pagemode > .body {
overflow: initial;
position: relative;
}
.app.pagemode .apponly {
display: none !important;
}
.app.appmode .pageonly {
display: none !important;
}
.app.fullscreen > .navbar { .app.fullscreen > .navbar {
top: -3.1rem; top: -3.1rem;
} }
@ -77,6 +91,10 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;}
margin-left: 1rem; margin-left: 1rem;
} }
.header-logo {
height:2em;
}
.user-menu { .user-menu {
transition: top 0.5s ease-out; transition: top 0.5s ease-out;
position: absolute; position: absolute;

View File

@ -5,13 +5,16 @@ import { Subscription , Observable } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators'; import { distinctUntilChanged } from 'rxjs/operators';
import { Store, Action } from '@ngrx/store'; import { Store, Action } from '@ngrx/store';
import { IUser } from '../../models/user'; import { IUser } from '../../models/user';
import { OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc';
//AppCommon //AppCommon
import { IEventMessage } from '../../models/event.message'; import { IEventMessage } from '../../models/event.message';
import { IListItem } from '../../models/list.item'; import { IListItem } from '../../models/list.item';
import { EventService } from '../../services/event.service'; import { EventService } from '../../services/event.service';
import { ItemTypeService } from '../../services/itemtype.service';
import * as commonActions from '../../actions/app-common.actions'; import * as commonActions from '../../actions/app-common.actions';
import { HealthCheckService } from '../../services/healthcheck.service'; import { HealthCheckService } from '../../services/healthcheck.service';
import { AppConfig } from '../../shared/app.config';
import * as appReducers from '../../reducers/app-common.reducer'; import * as appReducers from '../../reducers/app-common.reducer';
@ -34,32 +37,27 @@ export class AppComponent implements OnInit, OnDestroy {
public currentFolder: Observable<IListItem>; public currentFolder: Observable<IListItem>;
public folderParents: Observable<IListItem[]>; public folderParents: Observable<IListItem[]>;
public fullScreen: Observable<boolean>; public fullScreen: Observable<boolean> = this.store$.select(appReducers.selectGetFullScreen);
public isOnline: Observable<boolean>; public isOnline: Observable<boolean> = this.store$.select(appReducers.SelectGetIsOnline);
public routeLoading: Observable<boolean>; public routeLoading: Observable<boolean> = this.store$.select(appReducers.selectGetRouteLoading);
public menuVisible: Observable<boolean>; public menuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetMenuVisible);
public accountMenuVisible: Observable<boolean>; public accountMenuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetAccountMenuVisible);
public user:Observable<IUser>; public user: Observable<IUser> = this.store$.select(appReducers.SelectGetUser);
public isPageMode: Observable<boolean> = this.store$.select(appReducers.SelectGetIsPageMode);
@Input() showUploadProgress: boolean = true; @Input() showUploadProgress: boolean = true;
constructor( constructor(
private router: Router, public router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute$: ActivatedRoute,
private title: Title, private title$: Title,
private meta: Meta, private meta$: Meta,
private store: Store<appReducers.State>, private store$: Store<appReducers.State>,
private eventService: EventService, private eventService$: EventService,
private healthCheckService: HealthCheckService private healthCheckService$: HealthCheckService,
private itemTypeService$: ItemTypeService,
private oauthService$: OAuthService,
private appConfig$: AppConfig
) { ) {
//check health every 30 seconds
this.healthCheckService.check(30000).pipe(distinctUntilChanged()).subscribe((online) => {
if(online) {
this.store.dispatch(new commonActions.Online());
} else {
this.store.dispatch(new commonActions.Offline());
}
});
} }
@ -104,21 +102,20 @@ export class AppComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.fullScreen = this.store.select(appReducers.selectGetFullScreen);
this.isOnline = this.store.select(appReducers.SelectGetIsOnline);
this.routeLoading = this.store.select(appReducers.selectGetRouteLoading);
this.menuVisible = this.store.select(appReducers.SelectGetMenuVisible);
this.accountMenuVisible = this.store.select(appReducers.SelectGetAccountMenuVisible);
this.user = this.store.select(appReducers.SelectGetUser);
this.InstallRouteEventHandler(); this.InstallRouteEventHandler();
this.InstallEventServiceEventHandler(); this.InstallEventServiceEventHandler();
this.InstallAuthenticationEventHandler();
this.InstallHealthCheck();
//load item types
this.itemTypeService$.load(this.appConfig$)
} }
@HostListener('document:keyup', ['$event']) @HostListener('document:keyup', ['$event'])
onKeyUp(event: KeyboardEvent) { onKeyUp(event: KeyboardEvent) {
let x = event.keyCode; let x = event.keyCode;
if (x === 27) { if (x === 27) {
this.store.dispatch(new commonActions.Escape(true,false)); this.store$.dispatch(new commonActions.Escape(true, false));
} }
} }
@ -128,31 +125,62 @@ export class AppComponent implements OnInit, OnDestroy {
if (this.eventSub$) this.eventSub$.unsubscribe(); if (this.eventSub$) this.eventSub$.unsubscribe();
} }
private InstallAuthenticationEventHandler() {
// auth event handler
this.oauthService$.events.subscribe((event) => {
console.debug(event.type);
if (event.type == 'token_error' || event.type == 'silent_refresh_timeout' || event.type == 'logout') {
let e = event as OAuthErrorEvent;
let p = e.params as any;
if (event.type == 'silent_refresh_timeout' || event.type == 'logout' || (p.error && p.error == 'login_required')) {
console.debug("Session expired");
this.router.navigate(['loggedout'], { queryParams: { redirectTo: this.router.url } });
}
}
});
}
private InstallRouteEventHandler() { private InstallRouteEventHandler() {
var other = this; var other = this;
this.routerSub$ = this.router.events.subscribe(event => { this.routerSub$ = this.router.events.subscribe(event => {
if (event instanceof RouteConfigLoadStart) { if (event instanceof RouteConfigLoadStart) {
other.store.dispatch(new commonActions.StartRouteLoading()); other.store$.dispatch(new commonActions.StartRouteLoading());
}
if (event instanceof NavigationEnd && (event.url == "/" || event.url.startsWith("/content") )) {
other.store$.dispatch(new commonActions.SetPageMode(true));
} else if (event instanceof NavigationEnd) {
other.store$.dispatch(new commonActions.SetPageMode(false));
} }
if (event instanceof RouteConfigLoadEnd) { if (event instanceof RouteConfigLoadEnd) {
other.store.dispatch(new commonActions.EndRouteLoading()); other.store$.dispatch(new commonActions.EndRouteLoading());
} }
if (event instanceof NavigationStart) { if (event instanceof NavigationStart) {
other.store.dispatch(new commonActions.SetMenuVisible(false)); other.store$.dispatch(new commonActions.SetMenuVisible(false));
} }
}); });
} }
private InstallEventServiceEventHandler() { private InstallEventServiceEventHandler() {
var other = this; var other = this;
this.eventSub$ = this.eventService.event.subscribe(event => { this.eventSub$ = this.eventService$.event.subscribe(event => {
var action = other.getActionFromEvent(event); var action = other.getActionFromEvent(event);
if (action) other.store.dispatch(action); if (action) other.store$.dispatch(action);
});
}
private InstallHealthCheck() {
//check health every 30 seconds
this.healthCheckService$.check(30000).pipe(distinctUntilChanged()).subscribe((online) => {
if (online) {
this.store$.dispatch(new commonActions.Online());
} else {
this.store$.dispatch(new commonActions.Offline());
}
}); });
} }
handleClick(event: MouseEvent) { handleClick(event: MouseEvent) {
this.store.dispatch(new commonActions.Escape(false,true)); this.store$.dispatch(new commonActions.Escape(false, true));
} }
handleStopBubble(event: MouseEvent) { handleStopBubble(event: MouseEvent) {
@ -161,7 +189,11 @@ export class AppComponent implements OnInit, OnDestroy {
handleToggleMenu(event: MouseEvent) { handleToggleMenu(event: MouseEvent) {
event.stopPropagation(); event.stopPropagation();
this.store.dispatch(new commonActions.ToggleMenu()); this.store$.dispatch(new commonActions.ToggleMenu());
}
handleHome(event: MouseEvent) {
this.router.navigate(['/']);
} }
} }

View File

@ -21,7 +21,8 @@ export interface State {
userPackages: IPackages, userPackages: IPackages,
userSettingsRoot: IItem, userSettingsRoot: IItem,
accountMenuVisible: boolean, accountMenuVisible: boolean,
isOnline: boolean isOnline: boolean,
isPageMode:boolean
} }
export const initialState: State = { export const initialState: State = {
@ -36,7 +37,8 @@ export const initialState: State = {
userPackages: {}, userPackages: {},
userSettingsRoot: null, userSettingsRoot: null,
accountMenuVisible: false, accountMenuVisible: false,
isOnline: true isOnline: true,
isPageMode: true
} }
export function reducer(state = initialState, action: appCommonActions.Actions ): State { export function reducer(state = initialState, action: appCommonActions.Actions ): State {
@ -130,6 +132,10 @@ export function reducer(state = initialState, action: appCommonActions.Actions )
case appCommonActions.OFFLINE:{ case appCommonActions.OFFLINE:{
return tassign(state,{isOnline:false}); return tassign(state,{isOnline:false});
} }
case appCommonActions.SETPAGEMODE: {
let a = action as appCommonActions.SetPageMode;
return tassign(state,{isPageMode:a.pageMode});
}
default: { default: {
return state; return state;
} }
@ -148,6 +154,7 @@ export const getUserPackages = (state: State) => state.userPackages;
export const getUserSettingsRoot = (state: State) => state.userSettingsRoot; export const getUserSettingsRoot = (state: State) => state.userSettingsRoot;
export const getAccountMenuVisible = (state: State) => state.accountMenuVisible; export const getAccountMenuVisible = (state: State) => state.accountMenuVisible;
export const getIsOnline = (state: State) => state.isOnline; export const getIsOnline = (state: State) => state.isOnline;
export const getIsPageMode = (state: State) => state.isPageMode;
export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME); export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME);
@ -163,4 +170,5 @@ export const SelectGetUserPackages = createSelector(selectAppCommonState,getUser
export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot); export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot);
export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible); export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible);
export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline); export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline);
export const SelectGetIsPageMode = createSelector(selectAppCommonState,getIsPageMode);

View File

@ -12,26 +12,11 @@ export function appConfigFactory(injector:Injector, appConfig: AppConfig, oauthS
return (): Promise<any> => { return (): Promise<any> => {
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
appConfig.load().then(() => { appConfig.load().then(() => {
oauthService.events.subscribe((event) => {
console.debug(event.type);
if (event.type == 'token_error' || event.type == 'silent_refresh_timeout' || event.type == 'logout') {
let e = event as OAuthErrorEvent;
let p = e.params as any;
if (event.type == 'silent_refresh_timeout' || event.type == 'logout' || (p.error && p.error == 'login_required')) {
let router = injector.get(Router);
console.debug("Session expired");
router.navigate(['loggedout'], { queryParams: { redirectTo: router.url } });
}
}
});
oauthService.configure(authconfigFactory.getAuthConfig(appConfig)); oauthService.configure(authconfigFactory.getAuthConfig(appConfig));
oauthService.setStorage(authStorage); oauthService.setStorage(authStorage);
oauthService.setupAutomaticSilentRefresh(); oauthService.setupAutomaticSilentRefresh();
}).then(() => oauthService.loadDiscoveryDocument() }).then(() => oauthService.loadDiscoveryDocument()
).then(() => { ).then(() => resolve()).catch(() => reject());
itemtypeService.load(appConfig).then(() => resolve()).catch(() => reject());
})
}); });
} }
} }