Fix/last screen capture (#9484)

* fix: last screen capture on app starting up

* fix: window automatically grow after start timer at first time

* fix: image-viewer handle last capture image

* fix: image-viewer handle last capture image
This commit is contained in:
syns
2026-02-19 22:39:24 +01:00
committed by GitHub
parent a03e63fcfd
commit ca6250692d
4 changed files with 89 additions and 18 deletions
+9 -1
View File
@@ -359,6 +359,13 @@ export function ipcMainHandler(store, startServer, knex, config, timeTrackerWind
return LocalStore.getStore('appSetting').theme;
});
ipcMain.handle('GET_LAST_CAPTURE', async () => {
const lastCapture = await timerService.findLastCapture();
return {
timeSlotId: lastCapture?.timeslotId
};
});
pluginListeners();
}
@@ -1242,7 +1249,8 @@ export function removeAllHandlers() {
'DESKTOP_CAPTURER_GET_SOURCES',
'FINISH_SYNCED_TIMER',
'COLLECT_ACTIVITIES',
'START_SERVER'
'START_SERVER',
'GET_LAST_CAPTURE'
];
channels.forEach((channel: string) => {
ipcMain.removeHandler(channel);
@@ -36,6 +36,7 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
items = [];
item: any = {};
private readonly _showImageHandler = this.showImageEventHandler.bind(this);
constructor(
// private dialogRef: NbDialogRef<any>
private readonly _electronService: ElectronService,
@@ -49,9 +50,18 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
});
}
getImages(_, arg: any[]) {
async showImageEventHandler(_, arg) {
if (arg.timeSlotId) {
const screenshots: any[] = await this.getLastScreenshot(arg.timeSlotId);
this.getImages(screenshots);
} else {
this.getImages(arg.screenshots);
}
}
getImages(screenshots: any[]) {
this._ngZone.run(() => {
this.items = arg
this.items = screenshots
.sort((a, b) => {
const c: any = new Date(b.recordedAt);
const d: any = new Date(a.recordedAt);
@@ -67,13 +77,13 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this._electronService.ipcRenderer.on('show_image', this.getImages.bind(this));
this._electronService.ipcRenderer.on('show_image', this._showImageHandler);
this._electronService.ipcRenderer.send('image_view_ready');
this.active_index = 0;
}
ngOnDestroy(): void {
this._electronService.ipcRenderer.removeListener('show_image', this.getImages.bind(this));
this._electronService.ipcRenderer.removeListener('show_image', this._showImageHandler);
}
close() {
@@ -133,4 +143,16 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
public trackById(index: number, item: any): number {
return item.id;
}
private async getLastScreenshot(timeSlotId: string) {
try {
const lastTimeSlot = await this._imageViewerService.getTimeSlot({
timeSlotId
});
return lastTimeSlot?.screenshots || [];
} catch(err) {
console.error('[ImageViewer] showImageEventHandler error:', err);
return [];
}
}
}
@@ -1,27 +1,30 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { shareReplay, firstValueFrom, concatMap } from 'rxjs';
import { ImageCacheService } from '../services';
import { shareReplay, firstValueFrom, concatMap, map } from 'rxjs';
import { ImageCacheService, Store, TimeSlotCacheService } from '../services';
import { ITimeSlot } from '@gauzy/contracts';
import { API_PREFIX } from '../constants';
import { toParams } from '@gauzy/ui-core/common';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class ImageViewerService {
constructor(
private readonly _imageCacheService: ImageCacheService,
private readonly _domSanitizer: DomSanitizer,
private readonly _http: HttpClient
) { }
private readonly _http: HttpClient,
private readonly _store: Store,
private readonly _timeSlotCacheService: TimeSlotCacheService
) {}
public source(imageUrl: string): Promise<string> {
this._imageCacheService.clear();
let image$ = this._imageCacheService.getValue(imageUrl);
if (!image$) {
image$ = this._http.get(imageUrl, { responseType: 'blob' }).pipe(
concatMap((response: Blob) =>
this.getBase64ImageFromBlob(response)
),
concatMap((response: Blob) => this.getBase64ImageFromBlob(response)),
shareReplay(1)
);
this._imageCacheService.setValue(image$, imageUrl);
@@ -70,4 +73,36 @@ export class ImageViewerService {
return false;
}
}
async getTimeSlot(values: { timeSlotId: string }): Promise<ITimeSlot | null> {
const { timeSlotId } = values;
if (!timeSlotId) {
return null;
}
const { tenantId, organizationId } = this._store;
const params = toParams({
tenantId,
organizationId,
relations: ['screenshots'],
order: {
createdAt: 'DESC',
screenshots: {
recordedAt: 'DESC'
}
}
});
let timeSlots$ = this._timeSlotCacheService.getValue(timeSlotId);
if (!timeSlots$) {
timeSlots$ = this._http
.get<ITimeSlot>(`${API_PREFIX}/timesheet/time-slot/${timeSlotId}`, {
params
})
.pipe(
map((response: any) => response),
shareReplay(1)
);
this._timeSlotCacheService.setValue(timeSlots$, timeSlotId);
}
return firstValueFrom(timeSlots$);
}
}
@@ -1871,11 +1871,13 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
}
public async getLastTimeSlotImage(arg): Promise<void> {
if (this._isOffline || this.lastTimeSlot?.id === arg?.timeSlotId) {
return;
}
try {
const res = await this.timeTrackerService.getTimeSlot(arg);
const lastTimeSlot: { timeSlotId?: string } = await this.electronService.invoke('GET_LAST_CAPTURE');
if (this._isOffline || !lastTimeSlot?.timeSlotId) {
return;
}
const res = await this.timeTrackerService.getTimeSlot({ timeSlotId: lastTimeSlot.timeSlotId });
const { screenshots = [] } = res || {};
if (screenshots && screenshots.length > 0) {
const [lastCaptureScreen] = screenshots;
@@ -2006,7 +2008,11 @@ export class TimeTrackerComponent implements OnInit, AfterViewInit {
this._loggerService.warn(`WARN: ${message}`);
return;
}
this.electronService.ipcRenderer.send('show_image', this.screenshots);
this.electronService.ipcRenderer.send('show_image', {
screenshots: this.screenshots,
timeSlotId: !this._isOffline ? this.lastTimeSlot?.id : null
});
}
public open(dialog: TemplateRef<any>, option): void {