1- import { Injectable , Optional , NgZone , OnDestroy , ComponentFactoryResolver , Inject , PLATFORM_ID } from '@angular/core' ;
1+ import { Injectable , Optional , NgZone , OnDestroy , ComponentFactoryResolver , Inject , PLATFORM_ID , Injector , NgModuleFactory } from '@angular/core' ;
22import { Subscription , from , Observable , empty , of } from 'rxjs' ;
33import { filter , withLatestFrom , switchMap , map , tap , pairwise , startWith , groupBy , mergeMap } from 'rxjs/operators' ;
4- import { Router , NavigationEnd , ActivationEnd } from '@angular/router' ;
4+ import { Router , NavigationEnd , ActivationEnd , ROUTES } from '@angular/router' ;
55import { runOutsideAngular } from '@angular/fire' ;
6- import { AngularFireAnalytics } from './analytics' ;
6+ import { AngularFireAnalytics , DEBUG_MODE } from './analytics' ;
77import { User } from 'firebase/app' ;
88import { Title } from '@angular/platform-browser' ;
99import { isPlatformBrowser } from '@angular/common' ;
@@ -27,6 +27,8 @@ const DEFAULT_SCREEN_CLASS = '???';
2727const NG_PRIMARY_OUTLET = 'primary' ;
2828const SCREEN_INSTANCE_DELIMITER = '#' ;
2929
30+ const ANNOTATIONS = '__annotations__' ;
31+
3032@Injectable ( )
3133export class ScreenTrackingService implements OnDestroy {
3234
@@ -38,7 +40,9 @@ export class ScreenTrackingService implements OnDestroy {
3840 @Optional ( ) title :Title ,
3941 componentFactoryResolver : ComponentFactoryResolver ,
4042 @Inject ( PLATFORM_ID ) platformId :Object ,
41- zone : NgZone
43+ @Optional ( ) @Inject ( DEBUG_MODE ) debugModeEnabled :boolean | null ,
44+ zone : NgZone ,
45+ injector : Injector
4246 ) {
4347 if ( ! router || ! isPlatformBrowser ( platformId ) ) { return this }
4448 zone . runOutsideAngular ( ( ) => {
@@ -70,23 +74,40 @@ export class ScreenTrackingService implements OnDestroy {
7074 // it's lazy so it's not registered with componentFactoryResolver yet... seems a pain for a depreciated style
7175 return of ( { ...params , [ SCREEN_CLASS_KEY ] : loadChildren . split ( '#' ) [ 1 ] } ) ;
7276 } else if ( typeof component === 'string' ) {
73- // TODO figure out when this would this be a string
7477 return of ( { ...params , [ SCREEN_CLASS_KEY ] : component } ) ;
7578 } else if ( component ) {
7679 const componentFactory = componentFactoryResolver . resolveComponentFactory ( component ) ;
7780 return of ( { ...params , [ SCREEN_CLASS_KEY ] : componentFactory . selector } ) ;
7881 } else if ( loadChildren ) {
7982 const loadedChildren = loadChildren ( ) ;
80- var loadedChildren$ : Observable < any > ;
81- // TODO clean up this handling...
82- // can componentFactorymoduleType take an ngmodulefactory or should i pass moduletype?
83- try { loadedChildren$ = from ( zone . runOutsideAngular ( ( ) => loadedChildren as any ) ) } catch ( _ ) { loadedChildren$ = of ( loadedChildren as any ) }
84- return loadedChildren$ . pipe ( map ( child => {
85- const componentFactory = componentFactoryResolver . resolveComponentFactory ( child ) ;
86- return { ...params , [ SCREEN_CLASS_KEY ] : componentFactory . selector } ;
87- } ) ) ;
83+ var loadedChildren$ : Observable < any > = ( loadedChildren instanceof Observable ) ? loadedChildren : from ( Promise . resolve ( loadedChildren ) ) ;
84+ return loadedChildren$ . pipe (
85+ map ( lazyModule => {
86+ if ( lazyModule instanceof NgModuleFactory ) {
87+ // AOT create an injector
88+ const moduleRef = lazyModule . create ( injector ) ;
89+ // INVESTIGATE is this the right way to get at the matching route?
90+ const routes = moduleRef . injector . get ( ROUTES ) ;
91+ const component = routes [ 0 ] [ 0 ] . component ; // should i just be grabbing 0-0 here?
92+ try {
93+ const componentFactory = moduleRef . componentFactoryResolver . resolveComponentFactory ( component ! ) ;
94+ return { ...params , [ SCREEN_CLASS_KEY ] : componentFactory . selector } ;
95+ } catch ( _ ) {
96+ return { ...params , [ SCREEN_CLASS_KEY ] : DEFAULT_SCREEN_CLASS } ;
97+ }
98+ } else {
99+ // JIT look at the annotations
100+ // INVESTIGATE are there public APIs for this stuff?
101+ const declarations = [ ] . concat . apply ( [ ] , ( lazyModule [ ANNOTATIONS ] || [ ] ) . map ( ( f :any ) => f . declarations ) ) ;
102+ const selectors = [ ] . concat . apply ( [ ] , declarations . map ( ( c :any ) => ( c [ ANNOTATIONS ] || [ ] ) . map ( ( f :any ) => f . selector ) ) ) ;
103+ // should I just be grabbing the selector like this or should i match against the route component?
104+ // const routerModule = lazyModule.ngInjectorDef.imports.find(i => !!i.ngModule);
105+ // const route = routerModule.providers[0].find(p => p.provide == ROUTES).useValue[0];
106+ return { ...params , [ SCREEN_CLASS_KEY ] : selectors [ 0 ] || DEFAULT_SCREEN_CLASS } ;
107+ }
108+ } )
109+ ) ;
88110 } else {
89- // TODO figure out what forms of router events I might be missing
90111 return of ( { ...params , [ SCREEN_CLASS_KEY ] : DEFAULT_SCREEN_CLASS } ) ;
91112 }
92113 } ) ,
@@ -116,6 +137,7 @@ export class ScreenTrackingService implements OnDestroy {
116137 [ FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY ] : prior [ FIREBASE_SCREEN_INSTANCE_ID_KEY ] ,
117138 ...current !
118139 } : current ! ) ,
140+ tap ( params => debugModeEnabled && console . info ( SCREEN_VIEW_EVENT , params ) ) ,
119141 tap ( params => zone . runOutsideAngular ( ( ) => analytics . logEvent ( SCREEN_VIEW_EVENT , params ) ) )
120142 ) . subscribe ( ) ;
121143 } ) ;
0 commit comments