11import { ChangeDetectionStrategy , Component , EventEmitter , Inject , Input , OnInit , Output } from '@angular/core' ;
2- import { Router } from '@angular/router' ;
2+ import { NavigationStart , Router } from '@angular/router' ;
33
44import { BehaviorSubject , combineLatest , Observable , Subscription } from 'rxjs' ;
55import { debounceTime , distinctUntilChanged , filter , map , switchMap } from 'rxjs/operators' ;
@@ -11,7 +11,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
1111import { pushInOut } from '../animations/push' ;
1212import { HostWindowService } from '../host-window.service' ;
1313import { SidebarService } from '../sidebar/sidebar.service' ;
14- import { hasValue } from '../empty.util' ;
14+ import { hasValue , hasValueOperator , isNotEmpty } from '../empty.util' ;
1515import { RouteService } from '../../core/services/route.service' ;
1616import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component' ;
1717import { PaginatedSearchOptions } from './models/paginated-search-options.model' ;
@@ -35,6 +35,9 @@ import { environment } from 'src/environments/environment';
3535import { SubmissionObject } from '../../core/submission/models/submission-object.model' ;
3636import { SearchFilterConfig } from './models/search-filter-config.model' ;
3737import { WorkspaceItem } from '../..//core/submission/models/workspaceitem.model' ;
38+ import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths' ;
39+ import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths' ;
40+ import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths' ;
3841
3942@Component ( {
4043 selector : 'ds-search' ,
@@ -229,9 +232,21 @@ export class SearchComponent implements OnInit {
229232 searchLink : string ;
230233
231234 /**
232- * Subscription to unsubscribe from
235+ * Regex to match UUIDs
233236 */
234- sub : Subscription ;
237+ uuidRegex = / \b [ 0 - 9 a - f ] { 8 } \b - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - [ 0 - 9 a - f ] { 4 } - \b [ 0 - 9 a - f ] { 12 } \b / g;
238+
239+ /**
240+ * List of paths that are considered to be the start of a route to an object page (excluding "/", e.g. "items")
241+ * These are expected to end on an object UUID
242+ * If they match the route we're navigating to, an object property will be added to the search event sent
243+ */
244+ allowedObjectPaths : string [ ] = [ 'entities' , ITEM_MODULE_PATH , COLLECTION_MODULE_PATH , COMMUNITY_MODULE_PATH ] ;
245+
246+ /**
247+ * Subscriptions to unsubscribe from
248+ */
249+ subs : Subscription [ ] = [ ] ;
235250
236251 /**
237252 * Emits an event with the current search result entries
@@ -301,7 +316,7 @@ export class SearchComponent implements OnInit {
301316 ) ;
302317 const searchOptions$ : Observable < PaginatedSearchOptions > = this . getSearchOptions ( ) . pipe ( distinctUntilChanged ( ) ) ;
303318
304- this . sub = combineLatest ( [ configuration$ , searchSortOptions$ , searchOptions$ , sortOption$ ] ) . pipe (
319+ this . subs . push ( combineLatest ( [ configuration$ , searchSortOptions$ , searchOptions$ , sortOption$ ] ) . pipe (
305320 filter ( ( [ configuration , searchSortOptions , searchOptions , sortOption ] : [ string , SortOptions [ ] , PaginatedSearchOptions , SortOptions ] ) => {
306321 // filter for search options related to instanced paginated id
307322 return searchOptions . pagination . id === this . paginationId ;
@@ -332,7 +347,9 @@ export class SearchComponent implements OnInit {
332347 this . retrieveSearchResults ( newSearchOptions ) ;
333348 this . retrieveFilters ( searchOptions ) ;
334349 }
335- } ) ;
350+ } ) ) ;
351+
352+ this . subscribeToRoutingEvents ( ) ;
336353 }
337354
338355 /**
@@ -374,12 +391,10 @@ export class SearchComponent implements OnInit {
374391 }
375392
376393 /**
377- * Unsubscribe from the subscription
394+ * Unsubscribe from the subscriptions
378395 */
379396 ngOnDestroy ( ) : void {
380- if ( hasValue ( this . sub ) ) {
381- this . sub . unsubscribe ( ) ;
382- }
397+ this . subs . filter ( ( sub ) => hasValue ( sub ) ) . forEach ( ( sub ) => sub . unsubscribe ( ) ) ;
383398 }
384399
385400 /**
@@ -440,6 +455,43 @@ export class SearchComponent implements OnInit {
440455 } ) ;
441456 }
442457
458+ /**
459+ * Subscribe to routing events to detect when a user moves away from the search page
460+ * When the user is routing to an object page, it needs to send out a separate search event containing that object's UUID
461+ * This method should only be called once and is essentially what SearchTrackingComponent used to do (now removed)
462+ * @private
463+ */
464+ private subscribeToRoutingEvents ( ) {
465+ this . subs . push (
466+ this . router . events . pipe (
467+ filter ( ( event ) => event instanceof NavigationStart ) ,
468+ map ( ( event : NavigationStart ) => this . getDsoUUIDFromUrl ( event . url ) ) ,
469+ hasValueOperator ( ) ,
470+ ) . subscribe ( ( uuid ) => {
471+ if ( this . resultsRD$ . value . hasSucceeded ) {
472+ this . service . trackSearch ( this . searchOptions$ . value , this . resultsRD$ . value . payload as SearchObjects < DSpaceObject > , uuid ) ;
473+ }
474+ } ) ,
475+ ) ;
476+ }
477+
478+ /**
479+ * Get the UUID from a DSO url
480+ * Return null if the url isn't an object page (allowedObjectPaths) or the UUID couldn't be found
481+ * @param url
482+ */
483+ private getDsoUUIDFromUrl ( url : string ) : string {
484+ if ( isNotEmpty ( url ) ) {
485+ if ( this . allowedObjectPaths . some ( ( path ) => url . startsWith ( `/${ path } ` ) ) ) {
486+ const uuid = url . substring ( url . lastIndexOf ( '/' ) + 1 ) ;
487+ if ( uuid . match ( this . uuidRegex ) ) {
488+ return uuid ;
489+ }
490+ }
491+ }
492+ return null ;
493+ }
494+
443495 /**
444496 * Check if the sidebar is collapsed
445497 * @returns {Observable<boolean> } emits true if the sidebar is currently collapsed, false if it is expanded
0 commit comments