From 3b52cbe2f3b692b2007fcd5d755d20389e7152f1 Mon Sep 17 00:00:00 2001 From: Nicola Lanzilotto Date: Fri, 27 Dec 2024 16:41:50 +0100 Subject: [PATCH] Fixed the bug: if a new changelog notification is opened from the popup, the badge indicating the number of new changelog remains visible --- src/app/app.component.ts | 178 +++++++++++++----- src/app/components/navbar/navbar.component.ts | 43 +++-- src/app/services/sleekplan-api.service.ts | 7 + 3 files changed, 167 insertions(+), 61 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b0832e38727f..3630bd698a25 100755 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -38,6 +38,7 @@ import { ProjectService } from './services/project.service'; import { HttpClient } from '@angular/common/http'; import { SleekplanSsoService } from './services/sleekplan-sso.service'; import { SleekplanService } from './services/sleekplan.service'; +import { SleekplanApiService } from './services/sleekplan-api.service'; // import { UsersService } from './services/users.service'; @@ -103,9 +104,11 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { private notify: NotifyService, public usersLocalDbService: LocalDbService, private projectService: ProjectService, - + private sleekplanSsoService: SleekplanSsoService, - private sleekplanService: SleekplanService + private sleekplanService: SleekplanService, + private sleekplanApiService: SleekplanApiService + // public usersService: UsersService, // private faqKbService: FaqKbService, ) { @@ -234,16 +237,97 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { } else { this.logger.log('[APP-COMPONENT] There is no logged in user') } + + // -------------------------- + // Get if page is refreshed + // -------------------------- this.subscription = router.events.subscribe((event) => { if (event instanceof NavigationStart) { browserRefresh = !router.navigated; + this.logger.log('APP-COMP browserRefresh ', browserRefresh) + if (browserRefresh === true) { + window.addEventListener('load', () => { + this.logger.log('Page fully loaded'); + + // Check if there is the sleelplan chagenlog live announcemnt popup + this.checkSPPopupIframeWithRetries(3, 1000); // Retry 3 times with a 1-second delay + }); + + + } } }); - + this.loadStyle(JSON.parse(localStorage.getItem('custom_style'))) + + } + + checkSPPopupIframeWithRetries = (maxRetries = 3, delay = 1000) => { + let attempts = 0; + + const checkForIframe = () => { + attempts++; + this.logger.log(`Attempt ${attempts} to find the iframe...`); + + const wrapper = document.getElementById('sleek-widget-wrap'); + + if (wrapper) { + this.logger.log('wrapper', wrapper) + this.observeClassChange('.i-sl-wrapper.right.popup.active', 'expanded', () => { + this.logger.log('The "expanded" class was added!'); + this.sleekplanApiService.hasOpenedSPChangelogFromPopup() + }); + + return; // Stop further retries once the iframe is found + } + + if (attempts < maxRetries) { + setTimeout(checkForIframe, delay); + } else { + this.logger.log('Max attempts reached. Iframe not found.'); + } + }; + + checkForIframe(); + }; + + + observeClassChange(targetSelector: string, classToDetect: string, callback: () => void): void { + const targetElement = document.querySelector(targetSelector); + this.logger.log('targetElement ', targetElement); + if (!targetElement) { + this.logger.error('Target element not found'); + return; + } + + // Create a MutationObserver instance + const observer = new MutationObserver((mutationsList) => { + + for (const mutation of mutationsList) { + this.logger.log('mutation ', mutation); + if ( + mutation.type === 'attributes' && + mutation.attributeName === 'class' + ) { + const element = mutation.target as HTMLElement; + + // Check if the target element has the specified class + if (element.classList.contains(classToDetect)) { + this.logger.log(`Class "${classToDetect}" added to the element`); + callback(); // Trigger the callback + observer.disconnect(); + } + } + } + }); + + // Observe the element for class attribute changes + observer.observe(targetElement, { attributes: true }); } + + async loadStyle(data) { if (!data || !data.parameter) { @@ -355,13 +439,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { // this.logger.log('[APP-COMPONENT] project from $ubscription ', project) // // this.current_selected_prjct = project // this.projectService.getProjects().subscribe((projects: any) => { - // console.log('[APP-COMPONENT] getProjects projects ', projects) + // this.logger.log('[APP-COMPONENT] getProjects projects ', projects) // if (projects) { // this.current_selected_prjct_user = projects.find(prj => prj.id_project.id === project._id); - // console.log('[APP-COMPONENT] current_selected_prjct_user ', this.current_selected_prjct_user) - + // this.logger.log('[APP-COMPONENT] current_selected_prjct_user ', this.current_selected_prjct_user) + // this.USER_ROLE = this.current_selected_prjct_user.role - // console.log('[APP-COMPONENT] USER_ROLE ', this.USER_ROLE) + // this.logger.log('[APP-COMPONENT] USER_ROLE ', this.USER_ROLE) // } // }) // } @@ -628,44 +712,44 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { // id: user._id, // name: user.firstname, // } - + this.sleekplanSsoService.getSsoToken(user).subscribe( - (response) => { - this.logger.log('[APP-COMP] sleekplanSso response ', response) - this.logger.log('[APP-COMP] sleekplanSso response token', response['token']) - - // Configure Sleekplan with SSO - // window['Sleekplan'] = { - // id: 'YOUR_SLEEKPLAN_ID', - // sso: response.token, - // }; - - // window['$sleek'].setUser({ - // token: response['token'], - // }); - - // window.document.addEventListener('sleek:init', () => { - // window['$sleek'].setUser({ token: response['token'] }); - // }, false); - - // window['$sleek'].sso = { token: response['token'] } - - window['SLEEK_USER'] = { token: response['token'] } - - // Load the Sleekplan widget - this.sleekplanService.loadSleekplan().then(() => { - this.logger.log('[APP-COMP] - Sleekplan successfully initialized'); - }) - .catch(err => { - this.logger.error('[APP-COMP] - Sleekplan initialization failed', err); - }); - }, - (error) => { - this.logger.error('[APP-COMP] - Failed to fetch Sleekplan SSO token', error); - - } + (response) => { + this.logger.log('[APP-COMP] sleekplanSso response ', response) + this.logger.log('[APP-COMP] sleekplanSso response token', response['token']) + + // Configure Sleekplan with SSO + // window['Sleekplan'] = { + // id: 'YOUR_SLEEKPLAN_ID', + // sso: response.token, + // }; + + // window['$sleek'].setUser({ + // token: response['token'], + // }); + + // window.document.addEventListener('sleek:init', () => { + // window['$sleek'].setUser({ token: response['token'] }); + // }, false); + + // window['$sleek'].sso = { token: response['token'] } + + window['SLEEK_USER'] = { token: response['token'] } + + // Load the Sleekplan widget + this.sleekplanService.loadSleekplan().then(() => { + this.logger.log('[APP-COMP] - Sleekplan successfully initialized'); + }) + .catch(err => { + this.logger.error('[APP-COMP] - Sleekplan initialization failed', err); + }); + }, + (error) => { + this.logger.error('[APP-COMP] - Failed to fetch Sleekplan SSO token', error); + + } ); - } + } getCurrentUserAndConnectToWs() { this.auth.user_bs.subscribe((user) => { @@ -913,11 +997,11 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { (this.route.indexOf('/createfaq') !== -1) || (this.route.indexOf('/cds') !== -1) || (this.route.indexOf('/desktop-access') !== -1) || - (this.route.indexOf('/desktop--access') !== -1) + (this.route.indexOf('/desktop--access') !== -1) ) { // && this.USER_ROLE === 'agent' - + elemNavbar.setAttribute('style', 'display:none;'); elemAppSidebar.setAttribute('style', 'display:none;'); // margin-top: -70px @@ -941,8 +1025,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy { } else { // elemSidebarWrapper.style.height = "calc(100vh - 44px)"; elemSidebarWrapper.style.height = "calc(100vh - 60px)"; - - + + } if (this.route.indexOf('/request-for-panel') !== -1) { diff --git a/src/app/components/navbar/navbar.component.ts b/src/app/components/navbar/navbar.component.ts index b944f93dc936..a85904ce5fa3 100755 --- a/src/app/components/navbar/navbar.component.ts +++ b/src/app/components/navbar/navbar.component.ts @@ -29,7 +29,7 @@ import { AppConfigService } from '../../services/app-config.service'; // import { public_Key } from '../../utils/util'; // import { environment } from '../../../environments/environment'; import { Subject } from 'rxjs'; -import { filter, takeUntil, throttleTime } from 'rxjs/operators' +import { filter, skip, takeUntil, throttleTime } from 'rxjs/operators' import { Subscription } from 'rxjs' // import brand from 'assets/brand/brand.json'; @@ -300,9 +300,10 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft this.getTestSiteUrl(); this.translateStrings(); this.listenHasDeleteUserProfileImage(); - + this.listenSoundPreference() + this.listenToLiveAnnouncementOpened() // this.listenToQuotasReachedInHome() // this.listenToWSRequestsDataCallBack() @@ -2012,7 +2013,7 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft this.displayLogoutModal = 'none'; } - + testExpiredSessionFirebaseLogout() { this.auth.testExpiredSessionFirebaseLogout(true) @@ -2038,7 +2039,7 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft openFeedback(): void { - // console.log('[NAVBAR] open Sleekplan ', window['Sleekplan']) + // this.logger.log('[NAVBAR] open Sleekplan ', window['Sleekplan']) // if (window['Sleekplan']?.open) { // window['Sleekplan'].open(); // }this.user @@ -2048,9 +2049,23 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft const lastSeen = Date.now() this.logger.log('[NAVBAR] open Sleekplan lastSeen ', lastSeen) // localStorage.setItem('lastSeenTimestamp', this.lastSeen.toString()); - localStorage.setItem(`lastSeenTimestamp-${this.user._id}`,lastSeen.toString()) + localStorage.setItem(`lastSeenTimestamp-${this.user._id}`, lastSeen.toString()) this.newChangelogCount = false - + + } + + listenToLiveAnnouncementOpened() { + this.sleekplanApi.hasOpenedChangelogfromPopup$ + .pipe( + takeUntil(this.unsubscribe$) + ) + .pipe(skip(1)) + .subscribe((hasSeenChangelog) => { + this.logger.log('[NAVBAR] listenToLiveAnnouncementOpened hasSeenChangelog', hasSeenChangelog); + const lastSeen = Date.now() + localStorage.setItem(`lastSeenTimestamp-${this.user._id}`, lastSeen.toString()) + this.newChangelogCount = false + }) } fetchNewChangelogCount(user) { @@ -2059,7 +2074,7 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft this.logger.log('[NAVBAR] changelog lastSeen form storedLastSeen', storedLastSeen); this.logger.log('[NAVBAR] changelog lastSeen form storage type of', typeof storedLastSeen); let lastSeen = 0 - if (storedLastSeen !== null ) { + if (storedLastSeen !== null) { lastSeen = +storedLastSeen } this.sleekplanApi.getNewChangelogCount().subscribe( @@ -2069,26 +2084,26 @@ export class NavbarComponent extends PricingBaseComponent implements OnInit, Aft this.logger.log('[NAVBAR] changelog count resp data ', resp['data']); this.logger.log('[NAVBAR] changelog count resp data items ', resp['data']['items']); const data = resp['data']['items'] - + const firstKey = Object.keys(data)[0]; // Get the first key in the object const createdValue = data[firstKey].created; // Access the created property - this.logger.log('[NAVBAR] last changelog createdValue ', createdValue); + this.logger.log('[NAVBAR] last changelog createdValue ', createdValue); const createdValueTimestamp = new Date(createdValue).getTime(); this.logger.log('[NAVBAR] last changelog createdValue as Timestamp ', createdValueTimestamp); this.logger.log('[NAVBAR] lastSeen ', lastSeen); - if (lastSeen ) { + if (lastSeen) { if (createdValueTimestamp > lastSeen) { this.newChangelogCount = true - this.logger.log('[NAVBAR] there is A notification Changelog created at', createdValueTimestamp, ' last seen ', lastSeen , ' newChangelogCount ', this.newChangelogCount); + this.logger.log('[NAVBAR] there is A notification Changelog created at', createdValueTimestamp, ' last seen ', lastSeen, ' newChangelogCount ', this.newChangelogCount); } else { this.newChangelogCount = false - this.logger.log('[NAVBAR] there is NOT notification Changelog created at', createdValueTimestamp, ' last seen ', lastSeen , ' newChangelogCount ', this.newChangelogCount); + this.logger.log('[NAVBAR] there is NOT notification Changelog created at', createdValueTimestamp, ' last seen ', lastSeen, ' newChangelogCount ', this.newChangelogCount); } } else { this.newChangelogCount = true; - this.logger.log('[NAVBAR] there is A notification (else) Changelog created at', createdValueTimestamp, ' last seen ', lastSeen , ' newChangelogCount ', this.newChangelogCount); - + this.logger.log('[NAVBAR] there is A notification (else) Changelog created at', createdValueTimestamp, ' last seen ', lastSeen, ' newChangelogCount ', this.newChangelogCount); + } }, (error) => { diff --git a/src/app/services/sleekplan-api.service.ts b/src/app/services/sleekplan-api.service.ts index 34bdd279cc96..f24dfba20c85 100644 --- a/src/app/services/sleekplan-api.service.ts +++ b/src/app/services/sleekplan-api.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { LoggerService } from './logger/logger.service'; +import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) @@ -9,6 +10,8 @@ export class SleekplanApiService { SLEEKPLAN_API_KEY = '410017437c9fbea3a57b12346860fae9741ad0921'; // The good one // SLEEKPLAN_API_KEY = '9834831126380f3d69fff6251cd3a690cb97e41ac'; // for test SLEEKPLAN_API_URL = 'https://api.sleekplan.com/v1/updates'; + + public hasOpenedChangelogfromPopup$: BehaviorSubject = new BehaviorSubject(null); constructor( private httpClient: HttpClient, @@ -33,5 +36,9 @@ export class SleekplanApiService { return this.httpClient.get(url, httpOptions); } + hasOpenedSPChangelogFromPopup() { + this.hasOpenedChangelogfromPopup$.next(true) + } + }