oip-common 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,7 @@ import { PrimeNG } from 'primeng/config';
29
29
  import * as i3 from 'primeng/selectbutton';
30
30
  import { SelectButtonModule } from 'primeng/selectbutton';
31
31
  import { OidcSecurityService, PublicEventsService, EventTypes, StsConfigHttpLoader } from 'angular-auth-oidc-client';
32
- import { filter, map, switchMap, catchError } from 'rxjs/operators';
32
+ import { filter, map, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators';
33
33
  import { Tabs, TabList, Tab } from 'primeng/tabs';
34
34
  import * as i2$2 from 'primeng/avatar';
35
35
  import { AvatarModule } from 'primeng/avatar';
@@ -1475,6 +1475,10 @@ class KeycloakSecurityService extends OidcSecurityService {
1475
1475
  * Stores the decoded access token payload.
1476
1476
  */
1477
1477
  this.payload = new BehaviorSubject(null);
1478
+ /**
1479
+ * Stores user-specific data from the login response.
1480
+ */
1481
+ this.currentUser = new BehaviorSubject(null);
1478
1482
  this.publicEventsService
1479
1483
  .registerForEvents()
1480
1484
  .pipe(filter((event) => event.type === EventTypes.NewAuthenticationResult))
@@ -1483,14 +1487,17 @@ class KeycloakSecurityService extends OidcSecurityService {
1483
1487
  });
1484
1488
  }
1485
1489
  getCurrentUser() {
1486
- return this.userData;
1490
+ return this.currentUser.getValue();
1491
+ }
1492
+ getCurrentUser$() {
1493
+ return this.currentUser.asObservable();
1487
1494
  }
1488
1495
  /**
1489
1496
  * Returns the ID token for the sign-in.
1490
1497
  * @returns A string with the id token.
1491
1498
  */
1492
1499
  getAccessToken() {
1493
- return this.loginResponse.pipe(map((data) => data?.accessToken));
1500
+ return super.getAccessToken();
1494
1501
  }
1495
1502
  /**
1496
1503
  * Indicates whether the current user has the 'admin' role.
@@ -1507,7 +1514,7 @@ class KeycloakSecurityService extends OidcSecurityService {
1507
1514
  auth() {
1508
1515
  super.checkAuth().subscribe((_response) => {
1509
1516
  this.loginResponse.next(_response);
1510
- this.userData = _response.userData;
1517
+ this.currentUser.next(_response.userData);
1511
1518
  this.getPayloadFromAccessToken().subscribe((_token) => {
1512
1519
  this.payload.next(_token);
1513
1520
  });
@@ -1528,6 +1535,7 @@ class KeycloakSecurityService extends OidcSecurityService {
1528
1535
  ngOnDestroy() {
1529
1536
  this.loginResponse.complete();
1530
1537
  this.payload.complete();
1538
+ this.currentUser.complete();
1531
1539
  }
1532
1540
  /**
1533
1541
  * Checks whether the current access token is expired based on the 'exp' claim.
@@ -1554,6 +1562,7 @@ class UserService {
1554
1562
  constructor() {
1555
1563
  this.securityService = inject(SecurityService);
1556
1564
  this.baseDataService = inject(BaseDataService);
1565
+ this.requestedPhotoEmail = null;
1557
1566
  /**
1558
1567
  * Stores the user's photo as a data URL or binary blob, depending on how it's processed.
1559
1568
  */
@@ -1562,7 +1571,12 @@ class UserService {
1562
1571
  * Indicates whether the user photo has finished loading.
1563
1572
  */
1564
1573
  this.photoLoaded = false;
1565
- this.getUserPhoto();
1574
+ this.securityService
1575
+ .getCurrentUser$()
1576
+ .pipe(map((user) => user?.email ?? null), filter((email) => !!email), distinctUntilChanged())
1577
+ .subscribe(() => {
1578
+ this.getUserPhoto();
1579
+ });
1566
1580
  }
1567
1581
  /**
1568
1582
  * Returns a short label composed of the user's initials.
@@ -1570,23 +1584,34 @@ class UserService {
1570
1584
  */
1571
1585
  get shortLabel() {
1572
1586
  const data = this.securityService.getCurrentUser();
1573
- return data ? data.given_name[0].toUpperCase() + data.family_name[0].toUpperCase() : '';
1587
+ const givenNameInitial = data?.given_name?.trim()?.[0];
1588
+ const familyNameInitial = data?.family_name?.trim()?.[0];
1589
+ return `${givenNameInitial ?? ''}${familyNameInitial ?? ''}`.toUpperCase();
1574
1590
  }
1575
1591
  get userName() {
1576
1592
  const data = this.securityService.getCurrentUser();
1577
- return `${data.given_name} ${data.family_name}`;
1593
+ return [data?.given_name, data?.family_name].filter(Boolean).join(' ');
1578
1594
  }
1579
1595
  /**
1580
1596
  * Initiates an HTTP request to fetch the user's photo based on their email,
1581
1597
  * and updates the `photo` and `photoLoaded` properties accordingly.
1582
1598
  */
1583
1599
  getUserPhoto() {
1584
- const url = this.baseDataService.buildUrl(`api/user-profile/get-user-photo?email=${this.securityService.getCurrentUser().email}`);
1600
+ const email = this.securityService.getCurrentUser()?.email;
1601
+ if (!email) {
1602
+ return;
1603
+ }
1604
+ if (this.requestedPhotoEmail === email && (this.photoLoaded || this.photo)) {
1605
+ return;
1606
+ }
1607
+ this.requestedPhotoEmail = email;
1608
+ const url = this.baseDataService.buildUrl(`api/user-profile/get-user-photo?email=${email}`);
1585
1609
  this.baseDataService.getBlob(url).then((data) => {
1586
1610
  this.createImageFromBlob(data);
1587
1611
  this.photoLoaded = true;
1588
1612
  }, (error) => {
1589
1613
  console.log(error);
1614
+ this.photoLoaded = false;
1590
1615
  });
1591
1616
  }
1592
1617
  /**
@@ -1980,15 +2005,18 @@ class HttpClient {
1980
2005
  this.layoutService = inject(LayoutService);
1981
2006
  this.baseUrl = "";
1982
2007
  this.securityData = null;
1983
- this.securityWorker = (securityData) => ({
1984
- headers: {
2008
+ this.securityWorker = (securityData) => {
2009
+ const headers = {
1985
2010
  "Accept-language": this.layoutService.language()
1986
2011
  ? this.layoutService.language()
1987
2012
  : "en",
1988
2013
  "X-Timezone": this.layoutService.timeZone(),
1989
- Authorization: `Bearer ${securityData}`,
1990
- },
1991
- });
2014
+ };
2015
+ if (securityData) {
2016
+ headers.Authorization = `Bearer ${securityData}`;
2017
+ }
2018
+ return { headers };
2019
+ };
1992
2020
  this.abortControllers = new Map();
1993
2021
  this.customFetch = (...fetchParams) => fetch(...fetchParams);
1994
2022
  this.baseApiParams = {
@@ -3120,7 +3148,8 @@ class MenuComponent {
3120
3148
  ];
3121
3149
  }
3122
3150
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3123
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MenuComponent, isStandalone: true, selector: "app-menu", providers: [MenuApi], viewQueries: [{ propertyName: "menuItemCreateDialogComponent", first: true, predicate: MenuItemCreateDialogComponent, descendants: true }, { propertyName: "menuItemEditDialogComponent", first: true, predicate: MenuItemEditDialogComponent, descendants: true }, { propertyName: "contextMenu", first: true, predicate: ContextMenu, descendants: true }], ngImport: i0, template: ` <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
3151
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MenuComponent, isStandalone: true, selector: "app-menu", providers: [MenuApi], viewQueries: [{ propertyName: "menuItemCreateDialogComponent", first: true, predicate: MenuItemCreateDialogComponent, descendants: true }, { propertyName: "menuItemEditDialogComponent", first: true, predicate: MenuItemEditDialogComponent, descendants: true }, { propertyName: "contextMenu", first: true, predicate: ContextMenu, descendants: true }], ngImport: i0, template: `
3152
+ <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
3124
3153
  <ul class="layout-menu">
3125
3154
  @for (item of menuService.menu; track item; let i = $index) {
3126
3155
  <ng-container>
@@ -3140,10 +3169,10 @@ class MenuComponent {
3140
3169
  }
3141
3170
  </ul>
3142
3171
  </div>
3143
- <p-contextMenu [target]="empty" />
3144
- @if (securityService.isAdmin) {
3145
- <menu-item-create-dialog />
3146
- <menu-item-edit-dialog />
3172
+ <p-contextMenu [target]="empty"/>
3173
+ @if (securityService.isAdmin()) {
3174
+ <menu-item-create-dialog/>
3175
+ <menu-item-edit-dialog/>
3147
3176
  }`, isInline: true, dependencies: [{ kind: "component", type: MenuItemComponent, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey", "menuItemCreateDialogComponent", "menuItemEditDialogComponent", "contextMenu"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i1$4.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: MenuItemCreateDialogComponent, selector: "menu-item-create-dialog", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: MenuItemEditDialogComponent, selector: "menu-item-edit-dialog", inputs: ["visible"], outputs: ["visibleChange"] }] }); }
3148
3177
  }
3149
3178
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MenuComponent, decorators: [{
@@ -3162,7 +3191,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3162
3191
  providers: [MenuApi],
3163
3192
  selector: 'app-menu',
3164
3193
  standalone: true,
3165
- template: ` <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
3194
+ template: `
3195
+ <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
3166
3196
  <ul class="layout-menu">
3167
3197
  @for (item of menuService.menu; track item; let i = $index) {
3168
3198
  <ng-container>
@@ -3182,10 +3212,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3182
3212
  }
3183
3213
  </ul>
3184
3214
  </div>
3185
- <p-contextMenu [target]="empty" />
3186
- @if (securityService.isAdmin) {
3187
- <menu-item-create-dialog />
3188
- <menu-item-edit-dialog />
3215
+ <p-contextMenu [target]="empty"/>
3216
+ @if (securityService.isAdmin()) {
3217
+ <menu-item-create-dialog/>
3218
+ <menu-item-edit-dialog/>
3189
3219
  }`
3190
3220
  }]
3191
3221
  }], propDecorators: { menuItemCreateDialogComponent: [{
@@ -5917,17 +5947,10 @@ class NotificationService {
5917
5947
  this.securityService = inject(SecurityService);
5918
5948
  this.msgService = inject(MsgService);
5919
5949
  this.frontendConfig = inject(OIP_FRONTEND_CONFIG);
5920
- this.securityService.getAccessToken().subscribe((token) => {
5921
- this.securityData = token;
5922
- if (token) {
5923
- this.connection.stop().then(() => {
5924
- this.connection.start().then();
5925
- });
5926
- }
5927
- });
5950
+ this.securityData = null;
5928
5951
  this.connection = new signalR.HubConnectionBuilder()
5929
5952
  .withUrl(this.resolveHubUrl(), {
5930
- accessTokenFactory: () => this.securityData,
5953
+ accessTokenFactory: () => this.securityData ?? '',
5931
5954
  skipNegotiation: true,
5932
5955
  transport: signalR.HttpTransportType.WebSockets
5933
5956
  })
@@ -5942,6 +5965,19 @@ class NotificationService {
5942
5965
  };
5943
5966
  this.msgService.add(opt);
5944
5967
  });
5968
+ this.securityService.getAccessToken().subscribe((token) => {
5969
+ this.securityData = token;
5970
+ if (!token) {
5971
+ return;
5972
+ }
5973
+ if (this.connection.state === signalR.HubConnectionState.Disconnected) {
5974
+ this.connection.start().catch((error) => console.error('Failed to start notification connection', error));
5975
+ return;
5976
+ }
5977
+ this.connection.stop().then(() => {
5978
+ this.connection.start().catch((error) => console.error('Failed to restart notification connection', error));
5979
+ });
5980
+ });
5945
5981
  }
5946
5982
  resolveHubUrl() {
5947
5983
  if (this.frontendConfig.notificationHubUrl) {