From d8940dca4747614714868f4985aca50c8e9688bb Mon Sep 17 00:00:00 2001 From: Dan Briem Date: Fri, 26 May 2023 16:24:54 +0000 Subject: [PATCH] LP#1996818 Issues Placing Holds from the Patron Record Set the hold target using the same cookie Angular uses when placing holds from AngularJS patron records. Clear the cookie and broadcast to all catalog tabs to remove the hold target when: - the Clear button for the hold target is pressed - the hold interface loads a different patron - a different Angular route loads - AngularJS app starts (left the Angular context) When a catalog tab is closed, clear the cookie and broadcast it so that any open catalog tabs can restore it. To test: 1. After loading the patch, build Angular and AngularJS 2. Place a hold from AngJS patron record, note target is set 3. Open multiple catalog tabs 5. Close one catalog tab, note target persists in other tabs 6. Click Clear button, note target is cleared in all tabs 7. Repeat steps 2-3, load a different patron in the hold interface, note target is cleared in all tabs 8. Repeat steps 2-3, click the home icon in the navbar, note target is cleared in all tabs 9. Repeat steps 2-3, click AngJS Check Out in the navbar, note target is cleared in all tabs Signed-off-by: Dan Briem --- .../eg2/src/app/staff/catalog/catalog.component.ts | 23 +++++++++- .../eg2/src/app/staff/catalog/catalog.service.ts | 49 ++++++++++++++++++++-- .../web/js/ui/default/staff/circ/patron/holds.js | 2 +- .../web/js/ui/default/staff/services/startup.js | 23 ++++++++++ 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts index 340a28dba2..db45f8adfc 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts @@ -1,12 +1,15 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, HostListener, OnDestroy, OnInit} from '@angular/core'; import {IdlObject} from '@eg/core/idl.service'; import {StaffCatalogService} from './catalog.service'; import {BasketService} from '@eg/share/catalog/basket.service'; +import {Subject, takeUntil} from 'rxjs'; @Component({ templateUrl: 'catalog.component.html' }) -export class CatalogComponent implements OnInit { +export class CatalogComponent implements OnInit, OnDestroy { + + private onDestroy = new Subject(); constructor( private basket: BasketService, @@ -19,6 +22,12 @@ export class CatalogComponent implements OnInit { // reset and updated as needed to apply new search parameters. this.staffCat.createContext(); + // listen for hold patron target changes from other tabs + // until there's a route change + this.staffCat.onChangeHoldPatron().pipe( + takeUntil(this.onDestroy) + ).subscribe(); + // Subscribe to these emissions so that we can force // change detection in this component even though the // hold-for value was modified by a child component. @@ -34,5 +43,15 @@ export class CatalogComponent implements OnInit { clearHoldPatron() { this.staffCat.clearHoldPatron(); } + + @HostListener('window:beforeunload') + onBeforeUnload(): void { + this.staffCat.onBeforeUnload(); + } + + ngOnDestroy(): void { + this.clearHoldPatron(); + this.onDestroy.next(); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts index 24d32df03b..eaff89e2f8 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts @@ -1,4 +1,4 @@ -import {Injectable, EventEmitter} from '@angular/core'; +import {Injectable, EventEmitter, NgZone} from '@angular/core'; import {Router, ActivatedRoute} from '@angular/router'; import {IdlObject} from '@eg/core/idl.service'; import {OrgService} from '@eg/core/org.service'; @@ -8,6 +8,8 @@ import {CatalogSearchContext} from '@eg/share/catalog/search-context'; import {BibRecordSummary} from '@eg/share/catalog/bib-record.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; import {StoreService} from '@eg/core/store.service'; +import {BroadcastService} from '@eg/share/util/broadcast.service'; +import {Observable, tap} from 'rxjs'; const HOLD_FOR_PATRON_KEY = 'eg.circ.patron_hold_target'; @@ -70,7 +72,9 @@ export class StaffCatalogService { private org: OrgService, private cat: CatalogService, private patron: PatronService, - private catUrl: CatalogUrlService + private catUrl: CatalogUrlService, + private broadcaster: BroadcastService, + private zone: NgZone ) { } createContext(): void { @@ -99,11 +103,50 @@ export class StaffCatalogService { this.applySearchDefaults(); } - clearHoldPatron() { + clearHoldPatron(broadcast: boolean = true) { + const removedTarget = this.holdForBarcode; + this.holdForUser = null; this.holdForBarcode = null; this.store.removeLoginSessionItem(HOLD_FOR_PATRON_KEY); this.holdForChange.emit(); + if (!broadcast) return; + + // clear hold patron on other tabs + this.broadcaster.broadcast( + HOLD_FOR_PATRON_KEY, { removedTarget } + ); + } + + onBeforeUnload(): void { + const closedTarget = this.holdForBarcode; + if (closedTarget) { + this.clearHoldPatron(false); + this.broadcaster.broadcast(HOLD_FOR_PATRON_KEY, + { closedTarget } + ); + } + } + + onChangeHoldPatron(): Observable { + return this.broadcaster.listen(HOLD_FOR_PATRON_KEY).pipe( + tap(({ removedTarget, closedTarget }) => { + if (removedTarget && this.holdForBarcode) { + // broadcaster doesn't trigger change detection, + // so trigger it manually + this.zone.run(() => this.clearHoldPatron(false)); + + } else if (closedTarget) { + // if hold target was unset by another tab, + // restore the hold target + if (closedTarget === this.holdForBarcode) { + this.store.setLoginSessionItem( + HOLD_FOR_PATRON_KEY, closedTarget + ); + } + } + }) + ); } cloneContext(context: CatalogSearchContext): CatalogSearchContext { diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js b/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js index 3173bfdb70..795455df79 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js @@ -146,7 +146,7 @@ function($scope, $q, $routeParams, egCore, egUser, patronSvc, $scope.place_hold = function() { - egCore.hatch.setLocalItem( + egCore.hatch.setLoginSessionItem( 'eg.circ.patron_hold_target', patronSvc.current.card().barcode()); $window.location.href = '/eg2/staff/catalog'; diff --git a/Open-ILS/web/js/ui/default/staff/services/startup.js b/Open-ILS/web/js/ui/default/staff/services/startup.js index 8592d07cb0..4a242c3506 100644 --- a/Open-ILS/web/js/ui/default/staff/services/startup.js +++ b/Open-ILS/web/js/ui/default/staff/services/startup.js @@ -86,6 +86,29 @@ function($q, $rootScope, $location, $window, egIDL, egAuth, egEnv , egOrg // of the startup routines when no valid token exists during startup. $rootScope.$on('egAuthExpired', function() {service.expiredAuthHandler()}); + // in case we just left an Angular context, clear the hold target + // and notify any open Angular catalog tabs to do the same + function clearHoldTarget() { + var patronHoldTarget = $cookies.get( + 'eg.circ.patron_hold_target' + ); + if (patronHoldTarget) { + $cookies.remove( + 'eg.circ.patron_hold_target' + ) + if (typeof BroadcastChannel !== 'undefined') { + var broadcaster = new BroadcastChannel( + 'eg.circ.patron_hold_target' + ); + broadcaster.postMessage( + { removedTarget: patronHoldTarget } + ); + broadcaster.close(); + } + } + } + clearHoldTarget(); + service.go = function () { if (service.promise) { // startup already started, return our existing promise -- 2.11.0