diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.ts b/src/app/components/shared/tag-cloud/tag-cloud.component.ts index 321e584bd6114b0afd243af555ec8efb86cb6017..ea9a710848e0d814a4fc9f2a754a40a99a82e209 100644 --- a/src/app/components/shared/tag-cloud/tag-cloud.component.ts +++ b/src/app/components/shared/tag-cloud/tag-cloud.component.ts @@ -28,6 +28,7 @@ import { TagCloudPopUpComponent } from './tag-cloud-pop-up/tag-cloud-pop-up.comp import { TagCloudDataService, TagCloudDataTagEntry } from '../../../services/util/tag-cloud-data.service'; import { WsRoomService } from '../../../services/websockets/ws-room.service'; import { CloudParameters, CloudTextStyle } from '../../../utils/cloud-parameters'; +import { SmartDebounce } from '../../../utils/smart-debounce'; class CustomPosition implements Position { left: number; @@ -91,8 +92,6 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { }; userRole: UserRole; data: TagComment[] = []; - debounceTimer = 0; - lastDebounceTime = 0; configurationOpen = false; isLoading = true; headerInterface = null; @@ -105,6 +104,7 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { private _calcCanvas: HTMLCanvasElement = null; private _calcRenderContext: CanvasRenderingContext2D = null; private _calcFont: string = null; + private readonly _smartDebounce = new SmartDebounce(1, 1_000); constructor(private commentService: CommentService, private langService: LanguageService, @@ -309,17 +309,7 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { if (this._currentSettings.sortAlphabetically && this.data.length) { this.updateAlphabeticalPosition(this.data); } - const debounceTime = 1_000; - const current = new Date().getTime(); - const diff = current - this.lastDebounceTime; - clearTimeout(this.debounceTimer); - if (diff >= debounceTime) { - this.redraw(dataUpdated); - } else { - this.debounceTimer = setTimeout(() => { - this.redraw(dataUpdated); - }, debounceTime - diff); - } + this._smartDebounce.call(() => this.redraw(dataUpdated)); } openTags(tag: CloudData): void { @@ -351,7 +341,6 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { if (this.child === undefined) { return; } - this.lastDebounceTime = new Date().getTime(); this.isLoading = false; if (!dataUpdate) { this.child.reDraw(); diff --git a/src/app/services/util/tag-cloud-data.service.ts b/src/app/services/util/tag-cloud-data.service.ts index fa0f425e0ce6ec2fd9bd124600dfb59d5a44c718..864adce64ab4e85370491b42d2c7e521a15c52a0 100644 --- a/src/app/services/util/tag-cloud-data.service.ts +++ b/src/app/services/util/tag-cloud-data.service.ts @@ -9,6 +9,7 @@ import { RoomDataService } from './room-data.service'; import { SpacyKeyword } from '../http/spacy.service'; import { UserRole } from '../../models/user-roles.enum'; import { CloudParameters } from '../../utils/cloud-parameters'; +import { SmartDebounce } from '../../utils/smart-debounce'; export interface TagCloudDataTagEntry { weight: number; @@ -57,8 +58,6 @@ export enum TagCloudCalcWeightType { byLengthAndVotes } -const DEBOUNCE_TIME = 3_000; - @Injectable({ providedIn: 'root' }) @@ -79,8 +78,7 @@ export class TagCloudDataService { private _adminData: TopicCloudAdminData = null; private _subscriptionAdminData: Subscription; private _currentFilter: CommentFilter; - private _debounceTimer = 0; - private _lastDebounceTime = new Date().getTime() - DEBOUNCE_TIME; + private readonly _smartDebounce = new SmartDebounce(200, 3_000); constructor(private _tagCloudAdmin: TopicCloudAdminService, private _roomDataService: RoomDataService) { @@ -296,18 +294,7 @@ export class TagCloudDataService { newData = new Map<string, TagCloudDataTagEntry>([...current] .sort(([_, aTagData], [__, bTagData]) => bTagData.weight - aTagData.weight)); } - const currentTime = new Date().getTime(); - const diff = currentTime - this._lastDebounceTime; - clearTimeout(this._debounceTimer); - if (diff >= DEBOUNCE_TIME) { - this._dataBus.next(newData); - this._lastDebounceTime = currentTime; - } else { - this._debounceTimer = setTimeout(() => { - this._dataBus.next(newData); - this._lastDebounceTime = new Date().getTime(); - }, DEBOUNCE_TIME - diff); - } + this._smartDebounce.call(() => this._dataBus.next(newData)); } private onReceiveAdminData(data: TopicCloudAdminData, update = false) { diff --git a/src/app/utils/smart-debounce.ts b/src/app/utils/smart-debounce.ts new file mode 100644 index 0000000000000000000000000000000000000000..718c7209c9af2eadb94b81ef39dc8c0720895439 --- /dev/null +++ b/src/app/utils/smart-debounce.ts @@ -0,0 +1,37 @@ +export class SmartDebounce { + private readonly minDelay: number; + private readonly maxDelay: number; + private debounceTimer; + private lastDebounceTime = new Date().getTime(); + private minDebounceCount = 0; + + constructor(minDelay: number, maxDelay: number) { + if (minDelay < 0) { + throw new Error('minDelay should be positive.'); + } else if (maxDelay < minDelay) { + throw new Error('maxDelay should be greater than minDelay'); + } + this.minDelay = minDelay; + this.maxDelay = maxDelay; + } + + call(func: () => void) { + clearTimeout(this.debounceTimer); + const current = new Date().getTime(); + const diff = current - this.lastDebounceTime; + const callFunc = () => { + this.lastDebounceTime = current; + this.minDebounceCount = 0; + func(); + }; + if (diff >= this.maxDelay) { + if (this.minDelay * this.minDebounceCount++ >= this.maxDelay) { + callFunc(); + } else { + this.debounceTimer = setTimeout(callFunc, this.minDelay); + } + } else { + this.debounceTimer = setTimeout(callFunc, Math.max(this.minDelay, this.maxDelay - diff)); + } + } +}