First functional version
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				FarmMaps.Develop/FarmMapsLib/develop This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	FarmMaps.Develop/FarmMapsLib/develop This commit looks good
				
			This commit is contained in:
		| @@ -2,12 +2,10 @@ | ||||
|   <div> | ||||
|  | ||||
|   </div> | ||||
|   <ng-container *ngIf="(mapState$|async) as mapState"> | ||||
|           <aol-view [zoom]="mapState.zoom" [rotation]="mapState.rotation"> | ||||
|               <aol-coordinate [x]="mapState.xCenter" [y]="mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate> | ||||
|               <fm-map-zoom-to-extent [extent]="extent$|async" [animate]="true"></fm-map-zoom-to-extent> | ||||
|             </aol-view> | ||||
|   </ng-container>     | ||||
|   <aol-view [zoom]="(mapState|async).zoom" [rotation]="(mapState|async).rotation"> | ||||
|      <aol-coordinate [x]="(mapState|async).xCenter" [y]="(mapState|async).yCenter" [srid]="'EPSG:4326'"></aol-coordinate> | ||||
|      <fm-map-zoom-to-extent [extent]="extent|async" [animate]="true"></fm-map-zoom-to-extent> | ||||
|   </aol-view> | ||||
|   <aol-interaction-default></aol-interaction-default> | ||||
|   <aol-interaction-dragrotateandzoom></aol-interaction-dragrotateandzoom> | ||||
|   <fm-map-item-layers [itemLayers]="baseLayers|async"></fm-map-item-layers> | ||||
|   | ||||
| @@ -38,7 +38,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   title: string = 'Map'; | ||||
|   public openedModalName: Observable<string>; | ||||
|   public itemTypes: Observable<{ [id: string]: IItemType }>; | ||||
|   public mapState$: Observable<IMapState>; | ||||
|   public mapState: Observable<IMapState>; | ||||
|   public features: Observable<Array<Feature>>; | ||||
|   public overlayLayers: Observable<Array<IItemLayer>>; | ||||
|   public selectedOverlayLayer: Observable<IItemLayer>; | ||||
| @@ -67,7 +67,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   public position: Observable<Position>; | ||||
|   public baseLayersCollapsed:boolean = true; | ||||
|   public overlayLayersCollapsed: boolean = true; | ||||
|   public extent$: Observable<Extent>; | ||||
|   public extent: Observable<Extent>; | ||||
|   @ViewChild('map') map; | ||||
|    | ||||
|   constructor(private store: Store<mapReducers.State | commonReducers.State>, private route: ActivatedRoute, private router: Router, private uploadService: ResumableFileUploadService, private serializeService: StateSerializerService, public itemTypeService: ItemTypeService, private location: Location, private geolocationService: GeolocationService ) { | ||||
| @@ -107,7 +107,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   ngOnInit() { | ||||
|     this.store.dispatch(new mapActions.Init()); | ||||
|     this.selectedFeatures.next({x:0,y:0,features:[]}); | ||||
|     this.mapState$ = this.store.select(mapReducers.selectGetMapState); | ||||
|     this.mapState = this.store.select(mapReducers.selectGetMapState); | ||||
|     this.parentCode = this.store.select(mapReducers.selectGetParentCode); | ||||
|     this.features = this.store.select(mapReducers.selectGetFeatures); | ||||
|     this.overlayLayers = this.store.select(mapReducers.selectGetOverlayLayers); | ||||
| @@ -126,16 +126,16 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|     this.menuVisible = this.store.select(mapReducers.selectGetMenuVisible); | ||||
|     this.openedModalName = this.store.select(commonReducers.selectOpenedModalName); | ||||
|     this.query = this.store.select(mapReducers.selectGetQuery); | ||||
|     this.extent$ = this.store.select(mapReducers.selectGetExtent); | ||||
|     this.extent = this.store.select(mapReducers.selectGetExtent); | ||||
|     this.selectedFeatures.next(null); | ||||
|     this.selectedItemLayer = this.store.select(mapReducers.selectGetSelectedItemLayer); | ||||
|     this.period = this.store.select(mapReducers.selectGetPeriod); | ||||
|     this.position = this.geolocationService.getCurrentPosition(); | ||||
|  | ||||
|     this.mapState$.pipe(withLatestFrom(this.queryState)).subscribe((state) => { | ||||
|     this.mapState.pipe(withLatestFrom(this.queryState)).subscribe((state) => { | ||||
|       this.replaceUrl(state[0], state[1], true); | ||||
|     }); | ||||
|     this.query.pipe(withLatestFrom(this.mapState$)).subscribe((state) => { | ||||
|     this.query.pipe(withLatestFrom(this.mapState)).subscribe((state) => { | ||||
|       this.replaceUrl(state[1], state[0],false); | ||||
|     }); | ||||
|   } | ||||
|   | ||||
| @@ -22,7 +22,6 @@ export class GeolocationService { | ||||
|       navigator.geolocation.watchPosition( | ||||
|         (position: Position) => { | ||||
|           observer.next(position); | ||||
|           observer.complete(); | ||||
|         }, | ||||
|         (error: PositionError) => { | ||||
|           console.log('Geolocation service: ' + error.message); | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
|     "@ngrx/store": "^7.2", | ||||
|     "tassign": "^1.0.0", | ||||
|     "bootstrap": "^4.3.1", | ||||
|     "@aspnet/signalr": "^1.1.4" | ||||
|     "@aspnet/signalr": "^1.1.4", | ||||
|     "font-awesome": "^4.7.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -32,6 +32,7 @@ import { AuthGuard } from './services/auth-guard.service'; | ||||
| import { NavBarGuard } from './services/nav-bar-guard.service'; | ||||
| import { FullScreenGuard } from './services/full-screen-guard.service'; | ||||
| import { SafePipe } from './shared/safe.pipe'; | ||||
| import { AppComponent} from './components/app/app.component'; | ||||
| import { AuthCallbackComponent } from './components/auth-callback/auth-callback.component'; | ||||
| import { AuthCallbackGuard } from './components/auth-callback/auth-callback.guard'; | ||||
| import { SessionClearedComponent } from './components/session-cleared/session-cleared.component'; | ||||
| @@ -70,6 +71,7 @@ export {FolderService, | ||||
|   SafePipe, | ||||
|   AuthCallbackComponent, | ||||
|   AuthCallbackGuard, | ||||
|   AppComponent, | ||||
|   SessionClearedComponent, | ||||
|   ResumableFileUploadService, | ||||
|   ResumableFileUploadComponent, | ||||
| @@ -108,6 +110,7 @@ export {FolderService, | ||||
|     DatePipe | ||||
|   ], | ||||
|   declarations: [ | ||||
|     AppComponent, | ||||
|     AuthCallbackComponent, | ||||
|     SidePanelComponent, | ||||
|     SafePipe, | ||||
| @@ -121,6 +124,7 @@ export {FolderService, | ||||
|     NgbModule, | ||||
|     UploadxModule, | ||||
|     CommonModule, | ||||
|     AppComponent, | ||||
|     ResumableFileUploadComponent,  | ||||
|     AuthCallbackComponent, | ||||
|     SidePanelComponent, | ||||
|   | ||||
							
								
								
									
										14
									
								
								projects/common/src/fm/components/app/app.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								projects/common/src/fm/components/app/app.component.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async)}"> | ||||
|   <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> | ||||
|     <a class="navbar-brand" [routerLink]="['/']">Farm Maps <span *ngIf="routeLoading|async"><i class="fa fa-spinner fa-spin"></i></span></a> | ||||
|     <ul class="navbar-nav"> | ||||
|       <li class="nav-item py-0"><a [routerLinkActive]="['active']" [routerLink]="['/map']" class="nav-link"><span><i class="fa fa-globe" aria-hidden="true"></i> Map</span></a></li> | ||||
|       <li class="nav-item py-0"><a [routerLinkActive]="['active']" [routerLink]="['/explorer']" class="nav-link"><span><i class="fa fa-folder" aria-hidden="true"></i> Explorer</span></a></li> | ||||
|     </ul> | ||||
|     <router-outlet name="menu"></router-outlet> | ||||
|   </nav> | ||||
|   <div class="body"> | ||||
|     <router-outlet></router-outlet> | ||||
|   </div> | ||||
|   <fm-resumable-file-upload></fm-resumable-file-upload> | ||||
| </div> | ||||
							
								
								
									
										72
									
								
								projects/common/src/fm/components/app/app.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								projects/common/src/fm/components/app/app.component.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| //@import "theme.scss"; | ||||
|  | ||||
| /* Import Bootstrap & Fonts */ | ||||
|  | ||||
| @import "~bootstrap/scss/bootstrap.scss"; | ||||
|  | ||||
|  | ||||
| // custom styles | ||||
|  | ||||
| .btn:focus { | ||||
|   box-shadow:none; | ||||
| } | ||||
|  | ||||
| .input-group > .form-control:focus { | ||||
|   z-index: auto; | ||||
| } | ||||
|  | ||||
| .form-control:focus { | ||||
|   box-shadow: none; | ||||
|   border-color: $input-border-color; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* *** Overall APP Styling can go here *** | ||||
|    -------------------------------------------- | ||||
|    Note: This Component has ViewEncapsulation.None so the styles will bleed out | ||||
|  | ||||
| */ | ||||
|  | ||||
| body { background: #f1f1f1; line-height: 18px; user-select:none;} | ||||
|  | ||||
| .navbar-brand { | ||||
|   padding-top: .5rem; | ||||
|   padding-bottom: .5rem; | ||||
| } | ||||
|  | ||||
| .app { | ||||
|   width:100vw; | ||||
|   height:100vh; | ||||
| } | ||||
|  | ||||
| .app > .navbar { | ||||
|   position: absolute; | ||||
|   transition: top 0.5s ease-out; | ||||
|   top:0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   height: 3.1rem; | ||||
| } | ||||
|  | ||||
| .app > .body { | ||||
|   position: absolute; | ||||
|   transition: top 0.5s ease-out; | ||||
|   top: 3.1rem; | ||||
|   bottom: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   overflow:hidden; | ||||
| } | ||||
|  | ||||
| .app.fullscreen > .navbar { | ||||
|   top: -3.1rem; | ||||
| } | ||||
|  | ||||
| .app.fullscreen > .body { | ||||
|   top:0; | ||||
| } | ||||
|  | ||||
| .btn-primary, .btn-primary:hover { | ||||
|   color: #ffffff; | ||||
| } | ||||
|  | ||||
							
								
								
									
										129
									
								
								projects/common/src/fm/components/app/app.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								projects/common/src/fm/components/app/app.component.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener } from '@angular/core'; | ||||
| import { Router, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; | ||||
| import { Meta, Title, DOCUMENT, MetaDefinition } from '@angular/platform-browser'; | ||||
| import { Subscription ,  Observable } from 'rxjs'; | ||||
| import { Store, Action } from '@ngrx/store'; | ||||
|  | ||||
| //AppCommon | ||||
| import { IEventMessage } from '../../models/event.message'; | ||||
| import { IListItem} from '../../models/list.item'; | ||||
| import { EventService } from '../../services/event.service'; | ||||
| import * as  commonActions  from '../../actions/app-common.actions'; | ||||
|  | ||||
| import * as appReducers from '../../reducers/app-common.reducer'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'fm-app', | ||||
|   templateUrl: './app.component.html', | ||||
|   styleUrls: ['./app.component.scss'], | ||||
|   encapsulation: ViewEncapsulation.None, | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush | ||||
| }) | ||||
| export class AppComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   // This will go at the END of your title for example "Home - Angular Universal..." <-- after the dash (-) | ||||
|   private endPageTitle: string = 'Farmmaps'; | ||||
|   // If no Title is provided, we'll use a default one before the dash(-) | ||||
|   private defaultPageTitle: string = 'Farmmaps'; | ||||
|  | ||||
|   private routerSub$: Subscription; | ||||
|   private eventSub$: Subscription; | ||||
|  | ||||
|   public currentFolder: Observable<IListItem>; | ||||
|   public folderParents: Observable<IListItem[]>; | ||||
|   public browseFileElement: any; | ||||
|   public browseDirectoryElement: any; | ||||
|   public fileDroptarget: any; | ||||
|   public fullScreen: Observable<boolean>; | ||||
|   public routeLoading: Observable<boolean>; | ||||
|  | ||||
|   constructor( | ||||
|     private router: Router, | ||||
|     private activatedRoute: ActivatedRoute, | ||||
|     private title: Title, | ||||
|     private meta: Meta, | ||||
|     private store: Store<appReducers.State>, | ||||
|     private eventService: EventService, | ||||
|   ) {     | ||||
|   } | ||||
|  | ||||
|   | ||||
|  | ||||
|   getActionFromEvent(event: IEventMessage): Action { | ||||
|     var action: Action = null; | ||||
|     console.log(`${event.eventType} Event received`); | ||||
|     switch (event.eventType) { | ||||
|       case "ItemChanged": { | ||||
|         action = new commonActions.ItemChangedEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "ItemAdded": { | ||||
|         action = new commonActions.ItemAddedEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "ItemDeleted": { | ||||
|         action = new commonActions.ItemDeletedEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "taskStart": { | ||||
|         action = new commonActions.TaskStartEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "taskEnd": { | ||||
|         action = new commonActions.TaskEndEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "taskError": { | ||||
|         action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return action; | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.fullScreen = this.store.select(appReducers.selectGetFullScreen); | ||||
|     this.routeLoading = this.store.select(appReducers.selectGetRouteLoading); | ||||
|     this.InstallRouteEventHandler(); | ||||
|     this.InstallEventServiceEventHandler(); | ||||
|   } | ||||
|  | ||||
|   @HostListener('document:keyup', ['$event']) | ||||
|   keyUp(event: KeyboardEvent) { | ||||
|     let x = event.keyCode; | ||||
|     if (x === 27) { | ||||
|       this.store.dispatch(new commonActions.Escape(true,false)); | ||||
|     }     | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     // Subscription clean-up | ||||
|     if(this.routerSub$) this.routerSub$.unsubscribe(); | ||||
|     if(this.eventSub$) this.eventSub$.unsubscribe(); | ||||
|   } | ||||
|  | ||||
|   private InstallRouteEventHandler() { | ||||
|     var other = this; | ||||
|     this.routerSub$ = this.router.events.subscribe(event => { | ||||
|       if (event instanceof RouteConfigLoadStart) { | ||||
|         other.store.dispatch(new commonActions.StartRouteLoading()); | ||||
|       } | ||||
|       if (event instanceof RouteConfigLoadEnd) { | ||||
|         other.store.dispatch(new commonActions.EndRouteLoading()); | ||||
|       }       | ||||
|     });      | ||||
|   } | ||||
|  | ||||
|   private InstallEventServiceEventHandler() { | ||||
|     var other = this; | ||||
|     this.eventSub$ = this.eventService.event.subscribe(event => { | ||||
|       var action = other.getActionFromEvent(event); | ||||
|       if (action) other.store.dispatch(action); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   handleClick(event: MouseEvent) { | ||||
|     this.store.dispatch(new commonActions.Escape(false,true)); | ||||
|   }   | ||||
| } | ||||
|  | ||||
| @@ -12,7 +12,9 @@ export interface State { | ||||
|   initialized: boolean, | ||||
|   rootItems: IListItem[], | ||||
|   itemTypes: IItemTypes, | ||||
|   user:IUser | ||||
|   user:IUser, | ||||
|   fullScreen: boolean, | ||||
|   routeLoading:boolean | ||||
| } | ||||
|  | ||||
| export const initialState: State = { | ||||
| @@ -20,7 +22,9 @@ export const initialState: State = { | ||||
|   initialized: false, | ||||
|   rootItems: [], | ||||
|   itemTypes: {}, | ||||
|   user:null | ||||
|   user:null, | ||||
|   fullScreen: true, | ||||
|   routeLoading: false | ||||
| } | ||||
|  | ||||
| export function reducer(state = initialState, action: appCommonActions.Actions ): State { | ||||
| @@ -46,6 +50,26 @@ export function reducer(state = initialState, action: appCommonActions.Actions ) | ||||
|       let a = action as appCommonActions.LoadItemTypesSuccess; | ||||
|       return tassign(state, { itemTypes: a.itemTypes }); | ||||
|     } | ||||
|     case appCommonActions.FULLSCREEN: { | ||||
|       return tassign(state, { | ||||
|         fullScreen:true | ||||
|       }); | ||||
|     } | ||||
|     case appCommonActions.SHOWNAVBAR: { | ||||
|       return tassign(state, { | ||||
|         fullScreen: false | ||||
|       }); | ||||
|     } | ||||
|     case appCommonActions.STARTROUTELOADING: { | ||||
|       return tassign(state, { | ||||
|         routeLoading: true | ||||
|       }); | ||||
|     } | ||||
|     case appCommonActions.ENDROUTELOADING: { | ||||
|       return tassign(state, { | ||||
|         routeLoading: false | ||||
|       }); | ||||
|     } | ||||
|     default: { | ||||
|       return state; | ||||
|     } | ||||
| @@ -56,6 +80,9 @@ export const getOpenedModalName = (state: State) => state.openedModalName; | ||||
| export const getInitialized = (state: State) => state.initialized; | ||||
| export const getItemTypes = (state: State) => state.itemTypes; | ||||
| export const getRootItems = (state: State) => state.rootItems; | ||||
| export const getFullScreen = (state: State) => state.fullScreen; | ||||
| export const getRouteLoading = (state: State) => state.routeLoading; | ||||
|  | ||||
|  | ||||
| export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME); | ||||
|  | ||||
| @@ -63,3 +90,6 @@ export const selectOpenedModalName = createSelector(selectAppCommonState, getOpe | ||||
| export const selectGetInitialized = createSelector(selectAppCommonState, getInitialized); | ||||
| export const selectGetItemTypes = createSelector(selectAppCommonState, getItemTypes); | ||||
| export const selectGetRootItems = createSelector(selectAppCommonState, getRootItems); | ||||
| export const selectGetFullScreen = createSelector(selectAppCommonState, getFullScreen); | ||||
| export const selectGetRouteLoading = createSelector(selectAppCommonState, getRouteLoading); | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; | ||||
| import { IEventMessage } from '../models/event.message'; | ||||
| import { Subject } from 'rxjs'; | ||||
| import { OAuthService } from 'angular-oauth2-oidc'; | ||||
| import { HubConnection, HubConnectionBuilder, LogLevel } from '@aspnet/signalr'; | ||||
| import { HubConnection, HubConnectionBuilder, LogLevel ,HttpTransportType} from '@aspnet/signalr'; | ||||
| import { AppConfig } from "../shared/app.config"; | ||||
|  | ||||
|  | ||||
| @@ -15,15 +15,21 @@ export class EventService { | ||||
|      | ||||
|   constructor(private oauthService: OAuthService, private appConfig: AppConfig) { | ||||
|     this._apiEndPoint = appConfig.getConfig("apiEndPoint"); | ||||
|     this._connection = new HubConnectionBuilder().withUrl(`${ this._apiEndPoint}/eventHub`).configureLogging(LogLevel.Information).build(); | ||||
|     this._connection = new HubConnectionBuilder().withUrl(`${ this._apiEndPoint}/eventHub`, | ||||
|     { transport: HttpTransportType.WebSockets, | ||||
|       // accessTokenFactory: () => { | ||||
|       //   return oauthService.getAccessToken(); | ||||
|       // }, | ||||
|       skipNegotiation:true  | ||||
|     }).configureLogging(LogLevel.Information).build(); | ||||
|     this._connection.on('event', eventMessage => { | ||||
|       this.event.next(eventMessage); | ||||
|     }); | ||||
|     this._connection.start().then(() => { | ||||
|       var accessToken = oauthService.getAccessToken(); | ||||
|       if (accessToken) { | ||||
|         this._connection.send('authenticate', oauthService.getAccessToken()); | ||||
|       } | ||||
|     }); | ||||
|     this._connection.on('event', eventMessage => { | ||||
|       this.event.next(eventMessage); | ||||
|     }); | ||||
|     });     | ||||
|   } | ||||
| }  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user