Skip to content

Commit

Permalink
Merge pull request #7709 from ever-co/fix/screenshot-gallery-view
Browse files Browse the repository at this point in the history
[Fix] Screenshots Gallery Preview / Deletion
  • Loading branch information
rahul-rocket authored Mar 28, 2024
2 parents a9a7b2b + d24563f commit 27d3323
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 231 deletions.
26 changes: 14 additions & 12 deletions apps/gauzy/src/app/@shared/gallery/gallery.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
nbButton
status="primary"
[disabled]="active_index == 0"
(click)="prev($event)"
(click)="previous($event)"
>
<nb-icon icon="arrow-left"></nb-icon>
</button>
Expand All @@ -62,17 +62,19 @@
</div>
<div class="gallery-footer">
<div class="thumb-items custom-scroll" #customScroll>
<div *ngFor="let thumb of items; let index = index"
class="thumb-item text-center"
[ngClass]="{
'thumb-item-active': item == thumb,
'danger-bordered': thumb?.isWorkRelated === false
}"
(click)="setFocus(thumb)"
>
<img *ngIf="thumb" [src]="thumb?.thumbUrl" alt="Thumbnail Image" />
<span>{{ thumb?.recordedAt | dateTimeFormat }}</span>
</div>
<ng-container *ngFor="let thumb of items; let index = index; trackBy: trackByThumbId">
<div
class="thumb-item text-center"
[ngClass]="{
'thumb-item-active': item?.id === thumb?.id,
'danger-bordered': thumb?.isWorkRelated === false
}"
(click)="setFocus(thumb)"
>
<img *ngIf="thumb" [src]="thumb?.thumbUrl" alt="Thumbnail Image" />
<span>{{ thumb?.recordedAt | dateTimeFormat }}</span>
</div>
</ng-container>
</div>
</div>
</div>
154 changes: 112 additions & 42 deletions apps/gauzy/src/app/@shared/gallery/gallery.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { trigger, style, animate, transition } from '@angular/animations';
import { NbDialogRef } from '@nebular/theme';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { saveAs } from 'file-saver';
import { IEmployee } from '@gauzy/contracts';
import { GalleryItem } from './gallery.directive';
import { GalleryService } from './gallery.service';

Expand All @@ -33,103 +34,172 @@ export const fadeInOutAnimation = trigger('fadeInOut', [
animations: [fadeInOutAnimation]
})
export class GalleryComponent implements OnInit, OnDestroy {
active_index: any;

@ViewChild('customScroll', { static: true })
customScroll: ElementRef<HTMLElement>;
public active_index: number;
public items: GalleryItem[] = [];

@Input() item: GalleryItem;
items: GalleryItem[] = [];
@Input() employeeId: IEmployee['id'];

@Input() employeeId: string;
@ViewChild('customScroll', { static: true }) customScroll: ElementRef<HTMLElement>;

constructor(
private readonly dialogRef: NbDialogRef<GalleryComponent>,
private readonly galleryService: GalleryService
) { }

/**
* Initializes the component and subscribes to changes in the items emitted by the gallery service.
* Filters the items based on the employeeId property, if provided.
* Sets the items property and focuses on the active item.
*/
ngOnInit() {
// Subscribe to changes in the items emitted by the gallery service
this.galleryService.items$
.pipe(untilDestroyed(this))
.pipe(untilDestroyed(this)) // Unsubscribe when the component is destroyed
.subscribe((items) => {
// Filter the items based on the employeeId property, if provided
if (this.employeeId) {
this.items = items.filter(
(item: GalleryItem) => item.employeeId === this.employeeId
);
this.items = items.filter((item: GalleryItem) => item.employeeId === this.employeeId);
} else {
this.items = items;
}
// Set the focus on the active item
this.setFocus(this.item);
});
}

/**
* Closes the dialog.
* This function is typically called to close a dialog or modal window.
*/
close() {
// Close the dialog
this.dialogRef.close();
}

next($event) {
/**
* Handles navigation to the next item in the list.
* Stops event propagation to prevent parent event handlers from being triggered.
* Updates the active index to the next index and sets the active item accordingly.
* Ensures that the active item is visible within a scrollable container.
*
* @param $event The event object.
*/
next($event: PointerEvent) {
// Stop event propagation to prevent parent event handlers from being triggered
$event.stopPropagation();
this.active_index = Math.min(
this.active_index + 1,
this.items.length - 1
);

// Update the active index to the next index within the bounds of the item list
this.active_index = Math.min(this.active_index + 1, this.items.length - 1);

// Set the active item based on the updated active index
this.item = this.items[this.active_index];

// Ensure that the active item is visible within a scrollable container
this.updateActiveIndex();
}

prev($event) {
/**
* Handles navigation to the previous item in the list.
* Stops event propagation to prevent parent event handlers from being triggered.
* Updates the active index to the previous index and sets the active item accordingly.
* Ensures that the active item is visible within a scrollable container.
* @param $event The event object.
*/
previous($event: PointerEvent) {
// Stop event propagation to prevent parent event handlers from being triggered
$event.stopPropagation();

// Update the active index to the previous index within the bounds of the item list
this.active_index = Math.max(this.active_index - 1, 0);

// Set the active item based on the updated active index
this.item = this.items[this.active_index];

// Ensure that the active item is visible within a scrollable container
this.updateActiveIndex();
}

/**
* Sets the focus on a selected item in the gallery.
* If the selected item is found in the gallery, it becomes the active item.
* If not found, the provided item becomes the active item.
* Also updates the active index accordingly.
* @param selectedItem The item to set focus on.
*/
setFocus(selectedItem: GalleryItem) {
const foundItem = this.items.find(
(item) => item.fullUrl === selectedItem.fullUrl
);
if (this.item) {
const index = this.items.indexOf(this.item);
this.active_index = index;
// Find the item with the same fullUrl as the selectedItem
const foundItem = this.items.find((item) => item.id === selectedItem.id);

if (foundItem) {
// If the found item exists, set it as the active item and update the active index
this.item = foundItem;
this.active_index = this.items.indexOf(foundItem);
} else {
// If the selected item is not found in the gallery, set the provided item as the active item
this.item = selectedItem;
}

// Update the active index
this.updateActiveIndex();
}

/**
* Updates the active index to ensure that the active item is visible within the scrollable container.
* If the active item is not fully visible, scrolls the container to make it visible.
*/
updateActiveIndex() {
const activeItem = this.customScroll.nativeElement.querySelector(
'.thumb-item-active'
);
// Find the active item within the scrollable container
const activeItem = this.customScroll.nativeElement.querySelector('.thumb-item-active');

if (activeItem) {
// Get the position of the active item relative to the viewport
const position = activeItem.getBoundingClientRect();

if (position) {
const left: any = position.left;
const right: any = position.left + activeItem.clientWidth;
const scrollRight: any = this.customScroll.nativeElement
.clientWidth;
const scrollLeft: any = this.customScroll.nativeElement
.scrollLeft;
// Calculate the left and right boundaries of the active item
const left: number = position.left;
const right: number = position.left + activeItem.clientWidth;

// Get the width of the scrollable container
const scrollRight: number = this.customScroll.nativeElement.clientWidth;
// Get the current scroll position of the container
const scrollLeft: number = this.customScroll.nativeElement.scrollLeft;

// Check if the active item is fully visible
if (left < Math.abs(scrollLeft) || right > scrollRight) {
this.customScroll.nativeElement.scrollTo({
left: left
});
// If not fully visible, scroll the container to make it visible
this.customScroll.nativeElement.scrollTo({ left });
}
}
}
}

ngOnDestroy() { }

/**
* Downloads a file from the provided URL.
* @param url The URL of the file to download.
*/
downloadFile(url: string) {
if (url) {
this.galleryService
.downloadFile(url)
.subscribe(blob => {
saveAs(blob, url.replace(/^.*[\\\/]/, ''))
});
if (!url) {
return;
}

this.galleryService.downloadFile(url)
.subscribe(blob => {
const fileName = url.substring(url.lastIndexOf('/') + 1);
saveAs(blob, fileName);
});
}

/**
* Returns the unique identifier of a thumbnail object for tracking purposes.
* @param index The index of the current item in the array.
* @param thumb The thumbnail object being iterated over.
* @returns The unique identifier of the thumbnail object.
*/
trackByThumbId(index: number, thumb: any): any {
return thumb.id;
}

ngOnDestroy() { }
}
23 changes: 9 additions & 14 deletions apps/gauzy/src/app/@shared/gallery/gallery.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import {
} from '@angular/core';
import { NbDialogService } from '@nebular/theme';
import { sortBy } from 'underscore';
import { IScreenshot } from '@gauzy/contracts';
import { IEmployee, IScreenshot } from '@gauzy/contracts';
import { GalleryComponent } from './gallery.component';
import { GalleryService } from './gallery.service';

export interface GalleryItem {
id?: string;
thumbUrl: string;
fullUrl: string;
recordedAt?: Date;
Expand All @@ -30,9 +31,9 @@ export class GalleryDirective implements OnDestroy, OnInit {
public dialogRef: ComponentRef<GalleryComponent>;

// Inputs
@Input() employeeId: string;
@Input() item: GalleryItem | GalleryItem[];
@Input() items: GalleryItem[] = [];
@Input() item: GalleryItem;
@Input() employeeId: IEmployee['id'];

// Input with Setter
@Input() set disabled(value: any) {
Expand Down Expand Up @@ -61,18 +62,13 @@ export class GalleryDirective implements OnDestroy, OnInit {
}

// Deep copy the 'item' property
let items = JSON.parse(JSON.stringify(this.item));

// Sort the items array by 'createdAt' in descending order
items = sortBy(items, 'createdAt').reverse();

let item = JSON.parse(JSON.stringify(this.item));
// Extract the first item from the sorted array
const item = items instanceof Array ? items[0] : items;
item = item instanceof Array ? item[0] : item;

// Open a dialog (possibly a gallery) using NbDialogService
this.nbDialogService.open(GalleryComponent, {
context: {
items: this.items,
item,
employeeId: this.employeeId
},
Expand All @@ -82,13 +78,12 @@ export class GalleryDirective implements OnDestroy, OnInit {

ngOnInit() {
// Check if 'item' is an array; if not, convert it to a single-element array
const item = this.item instanceof Array ? this.item : [this.item];

const items = this.items instanceof Array ? this.items : [this.items];
// Sort the 'item' array by 'createdAt'
this.item = sortBy(item, 'createdAt');
this.items = sortBy(items, 'recordedAt');

// Append the sorted 'item' array to the gallery service
this.galleryService.appendItems(this.item);
this.galleryService.appendItems(this.items);
}

ngOnDestroy() { }
Expand Down
Loading

0 comments on commit 27d3323

Please sign in to comment.