mirror of
https://github.com/ever-co/ever-gauzy.git
synced 2026-10-02 01:54:50 +08:00
[Feat] Prevent Double Click on Start/Stop Button (#8516)
* feat: add BLOCK_DELAY constant `10 sec` * feat: add pulse animation to start/stop buttons, modified button styles, and refactore timer logic * fix: cspell
This commit is contained in:
@@ -5,6 +5,7 @@ import { environment } from '@gauzy/ui-config';
|
||||
export const API_PREFIX = '/api';
|
||||
export const BACKGROUND_SYNC_INTERVAL = 25000; // milliseconds
|
||||
export const BACKGROUND_SYNC_OFFLINE_INTERVAL = 5000; // milliseconds
|
||||
export const BLOCK_DELAY = 10000; // milliseconds
|
||||
|
||||
export const GAUZY_ENV = new InjectionToken<any>('gauzyEnvironment');
|
||||
|
||||
|
||||
@@ -58,18 +58,39 @@
|
||||
</div>
|
||||
<div class="col-3 d-flex flex-column align-items-center">
|
||||
<div>
|
||||
<button (click)="toggleStart(true)" *ngIf="!(start$ | async)"
|
||||
[disabled]="loading || !isTrackingEnabled" class="button-ghost" nbButton
|
||||
status="success">
|
||||
<nb-icon [nbTooltipDisabled]="isTrackingEnabled" [nbTooltipIcon]="{
|
||||
<button
|
||||
(click)="toggleStart(true)"
|
||||
*ngIf="!(start$ | async)"
|
||||
[ngClass]="{ 'pulse-border start-button': isProcessingEnabled }"
|
||||
[disabled]="loading || !isTrackingEnabled"
|
||||
class="button-ghost"
|
||||
nbButton
|
||||
status="success"
|
||||
>
|
||||
<nb-icon
|
||||
[nbTooltipDisabled]="isTrackingEnabled"
|
||||
[nbTooltipIcon]="{
|
||||
icon: 'alert-triangle-outline',
|
||||
pack: 'eva'
|
||||
}" [nbTooltip]="'TIMER_TRACKER.TIME_TRACKER_DISABLED' | translate" class="start-button" icon="play"
|
||||
nbTooltipPlacement="bottom" nbTooltipStatus="warning" pack="font-awesome">
|
||||
}"
|
||||
[nbTooltip]="'TIMER_TRACKER.TIME_TRACKER_DISABLED' | translate"
|
||||
class="start-button"
|
||||
icon="play"
|
||||
nbTooltipPlacement="bottom"
|
||||
nbTooltipStatus="warning"
|
||||
pack="font-awesome"
|
||||
>
|
||||
</nb-icon>
|
||||
</button>
|
||||
<button (click)="toggleStart(false)" *ngIf="start$ | async" [disabled]="loading"
|
||||
class="button-ghost" nbButton status="danger">
|
||||
<button
|
||||
(click)="toggleStart(false)"
|
||||
*ngIf="start$ | async"
|
||||
[disabled]="loading"
|
||||
[ngClass]="{ 'pulse-border stop-button': isProcessingEnabled }"
|
||||
class="button-ghost"
|
||||
nbButton
|
||||
status="danger"
|
||||
>
|
||||
<nb-icon class="start-button" icon="pause" pack="font-awesome"> </nb-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -741,3 +741,35 @@ img {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//animation
|
||||
@keyframes pulseBorder {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 var(--pulse-color-rgba);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 1rem 1rem rgba(var(--pulse-color-rgb), 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--pulse-color-rgb), 0);
|
||||
}
|
||||
}
|
||||
|
||||
.pulse-border {
|
||||
--pulse-duration: 1.5s;
|
||||
--pulse-color-rgb: 0, 255, 0; /* Default green color in RGB format */
|
||||
--pulse-color-rgba: rgba(var(--pulse-color-rgb), 0.4);
|
||||
--border-color: rgb(var(--pulse-color-rgb));
|
||||
|
||||
animation: pulseBorder var(--pulse-duration) infinite;
|
||||
}
|
||||
|
||||
.start-button {
|
||||
--pulse-color-rgb: 0, 255, 0; /* Green */
|
||||
--pulse-duration: 1s;
|
||||
}
|
||||
|
||||
.stop-button {
|
||||
--pulse-color-rgb: 255, 0, 0; /* Red */
|
||||
--pulse-duration: 1s;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,13 @@ import {
|
||||
Observable,
|
||||
of,
|
||||
Subject,
|
||||
tap
|
||||
Subscription,
|
||||
tap,
|
||||
timer
|
||||
} from 'rxjs';
|
||||
import { AlwaysOnService, AlwaysOnStateEnum } from '../always-on/always-on.service';
|
||||
import { AuthStrategy } from '../auth';
|
||||
import { GAUZY_ENV } from '../constants';
|
||||
import { BLOCK_DELAY, GAUZY_ENV } from '../constants';
|
||||
import { ElectronService, LoggerService } from '../electron/services';
|
||||
import { ImageViewerService } from '../image-viewer/image-viewer.service';
|
||||
import { ActivityWatchViewService } from '../integrations';
|
||||
@@ -115,6 +117,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
private _isReady = false;
|
||||
private _session: moment.Moment = null;
|
||||
private hasActiveTaskPermissions = false;
|
||||
private timerSubscription: Subscription | null = null;
|
||||
@ViewChild('dialogOpenBtn') btnDialogOpen: ElementRef<HTMLElement>;
|
||||
public start$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
userData: any;
|
||||
@@ -519,8 +522,6 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
this.isProcessingEnabled = false;
|
||||
|
||||
asapScheduler.schedule(async () => {
|
||||
try {
|
||||
await this.electronService.ipcRenderer.invoke('UPDATE_SYNCED_TIMER', {
|
||||
@@ -1391,11 +1392,25 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
: 'Please wait for timer to start and make initial screenshot';
|
||||
this._toastrNotifier.warn(message);
|
||||
this._loggerService.debug(message);
|
||||
return;
|
||||
} else {
|
||||
this.isProcessingEnabled = true;
|
||||
}
|
||||
this.loading = true;
|
||||
|
||||
if (this.timerSubscription) {
|
||||
this.timerSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this.timerSubscription = timer(BLOCK_DELAY)
|
||||
.pipe(untilDestroyed(this))
|
||||
.subscribe(() => {
|
||||
this.loading = false;
|
||||
this.isProcessingEnabled = false;
|
||||
this._loggerService.info('Processing Unlocked');
|
||||
this.timerSubscription = null;
|
||||
});
|
||||
|
||||
if (!val) {
|
||||
console.log('Stop tracking');
|
||||
|
||||
@@ -1515,6 +1530,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
this._loggerService.info('Capturing Screen and Sending Activities Start...', activities);
|
||||
await this.takeCaptureAndSendActivities(activities);
|
||||
this._loggerService.info('Capturing Screen and Sending Activities Done ✔️');
|
||||
this.isProcessingEnabled = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -1536,7 +1552,9 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
this._errorHandlerService.handleError(error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.isProcessingEnabled = false;
|
||||
if (this.isRemoteTimer) {
|
||||
this.isProcessingEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,6 +1608,7 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
await this.uploadScreenshots(activities, timeSlotId, screenshots);
|
||||
this._loggerService.info('Capturing Screen and Sending Activities Done ✔️');
|
||||
}
|
||||
this.isProcessingEnabled = false;
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
@@ -1628,7 +1647,9 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
|
||||
} finally {
|
||||
this._session = null;
|
||||
this.loading = false;
|
||||
this.isProcessingEnabled = false;
|
||||
if (this.isRemoteTimer) {
|
||||
this.isProcessingEnabled = false;
|
||||
}
|
||||
this.timeTrackerStore.ignition({ state: IgnitionState.STOPPED, mode: this._startMode });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user