Skip to content

Commit

Permalink
Allow deleting photos from albums
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjanssen committed Feb 11, 2024
1 parent abdc6ea commit e77b28b
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
<th class="id" scope="col">Id</th>
<th class="date" scope="col">Date</th>
<th scope="col" *ngIf="showGenerateImageLinks">img</th>
<th scope="col" *ngIf="showDeleteButton"><bs-icon name="trash"></bs-icon></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let photo of tablePhotos">
<td class="image"><img [src]="photo.srcSmall" class="image" /></td>
<td class="id">{{ photo.id }}</td>
<td class="date">{{ photo.date | date: 'dd-MM-yyyy - HH:mm' }}</td>
<td class="date">{{ photo.date | date : 'dd-MM-yyyy - HH:mm' }}</td>
<td *ngIf="showGenerateImageLinks">
<button class="btn btn-primary" (click)="generateImgHtml(photo.srcText, photo.srcLarge)">img</button>
<button class="btn btn-primary" (click)="onGenerateImgHtml(photo.srcText, photo.srcLarge)">img</button>
</td>
<th *ngIf="showDeleteButton"><bs-icon name="trash" (click)="onDelete(photo.id)"></bs-icon></th>
</tr>
</tbody>
</table>
11 changes: 9 additions & 2 deletions src/app/core/components/photo-table/photo-table.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core
import { Photo } from '../../types/photo.type';
import { EnvironmentService } from '../../services/environment.service';
import { NgIf, NgFor, DatePipe } from '@angular/common';
import { BootstrapIconComponent } from '../bootstrap-icon/bootstrap-icon.component';

@Component({
selector: 'photo-table',
templateUrl: './photo-table.component.html',
styleUrls: ['./photo-table.component.scss'],
standalone: true,
imports: [NgIf, NgFor, DatePipe]
imports: [NgIf, NgFor, DatePipe, BootstrapIconComponent]
})
export class PhotoTableComponent implements OnChanges {
@Input() photos: Photo[];
@Input() showGenerateImageLinks: boolean = false;
@Input() showDeleteButton: boolean = false;
@Output() imageHtml: EventEmitter<string> = new EventEmitter();
@Output() delete: EventEmitter<number> = new EventEmitter();
tablePhotos: { id: number; date: Date; srcSmall: string; srcText: string; srcLarge: string }[];

constructor(private readonly _environmentService: EnvironmentService) {}
Expand All @@ -22,7 +25,7 @@ export class PhotoTableComponent implements OnChanges {
this.tablePhotos = this.photos.map(photo => this.mapPhoto(photo, this._environmentService.baseApiUrl));
}

generateImgHtml(srcText: string, srcLarge: string): void {
onGenerateImgHtml(srcText: string, srcLarge: string): void {
this.imageHtml.emit(
`<a href="${srcLarge}" target="_blank" class="update-text-link">` +
`<img class="update-text-image" src="${srcText}">` +
Expand All @@ -31,6 +34,10 @@ export class PhotoTableComponent implements OnChanges {
);
}

onDelete(photoId: number): void {
this.delete.emit(photoId);
}

private mapPhoto(photo: Photo, baseApiUrl: string): { id: number; date: Date; srcSmall: string; srcText: string; srcLarge: string } {
const smallestImage = photo.images.sort((a, b) => a.widthPx - b.widthPx)[0];
const largestImage = photo.images.sort((a, b) => a.widthPx - b.widthPx).reverse()[0];
Expand Down
4 changes: 4 additions & 0 deletions src/app/core/services/album.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export class AlbumService {
return this._http.get<AlbumDetails>(this._getUrl(`GetById?id=${id}`));
}

deletePhoto(albumId: number, photoId: number): Observable<null> {
return this._http.delete<null>(this._getUrl('DeletePhoto'), { body: { albumId, photoId } });
}

private _getUrl(method: string): string {
return this._urlBuilderHelper.constructUrlWithApiUrlPrefix('v1/Album/' + method);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ <h5 class="card-title">Album content</h5>
</div>
</div>

<photo-table *ngIf="selectedAlbumDetails" [photos]="selectedAlbumDetails.photos"></photo-table>
<div class="alert alert-warning" *ngIf="showDeleteConfirmation">
<p>You are about to delete the following location:</p>
<ul>
<li><b>AlbumId:</b> {{ photoToDelete.albumId }}</li>
<li><b>Album name:</b> {{ photoToDelete.albumName | nullableDisplay }}</li>
<li><b>PhotoId:</b> {{ photoToDelete.photoId }}</li>
</ul>

<p>Are you sure? This action cannot be undone.</p>
<button (click)="confirmDelete()" class="btn btn-primary confirm">Confirm</button>
<button (click)="cancelDelete()" class="btn btn-secondary">Cancel</button>

<div class="delete-status-container" *ngIf="deleteResult$ | async as deleteResult">
<loading-message *ngIf="deleteResult | loading">Deleting location</loading-message>
<error-message *ngIf="deleteResult | error">An error occurred while deleting the location</error-message>
</div>
</div>

<photo-table
*ngIf="selectedAlbumDetails"
[showDeleteButton]="true"
(delete)="onPendingDelete($event)"
[photos]="selectedAlbumDetails.photos"
></photo-table>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,99 @@ import { Component } from '@angular/core';
import { AlbumService } from 'src/app/core/services/album.service';
import { Album, AlbumDetails } from 'src/app/core/types/album.type';
import { PhotoTableComponent } from '../../../../core/components/photo-table/photo-table.component';
import { NgFor, NgIf } from '@angular/common';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Observable, ReplaySubject, of, switchMap, tap } from 'rxjs';
import { DataStatus, StateStatus } from 'src/app/core/types/data-status.types';
import { DataStatusPipesModule } from 'src/app/core/pipes/status/data-status-pipes.module';
import { LoadingMessageComponent } from '../../../../core/components/loading-message/loading-message.component';
import { ErrorMessageComponent } from '../../../../core/components/error-message/error-message.component';
import { inspectStatus } from 'src/app/core/helpers/rxjs-operators';
import { WindowService } from 'src/app/core/services/window.service';
import { NullableDisplayPipe } from '../../../../core/pipes/nullable-display.pipe';

@Component({
selector: 'albums-overview',
templateUrl: './albums-overview.component.html',
styleUrls: ['./albums-overview.component.scss'],
standalone: true,
imports: [FormsModule, NgFor, NgIf, PhotoTableComponent]
imports: [
FormsModule,
NgFor,
NgIf,
PhotoTableComponent,
AsyncPipe,
DataStatusPipesModule,
LoadingMessageComponent,
ErrorMessageComponent,
NullableDisplayPipe
]
})
export class AlbumsOverviewComponent {
albums: Album[];
selectedAlbumId: number;
selectedAlbumId: string;
selectedAlbumDetails: AlbumDetails | null;

constructor(private readonly _albumService: AlbumService) {
deleted$: ReplaySubject<AlbumPhotoDelete | null> = new ReplaySubject<AlbumPhotoDelete | null>();
deleteResult$: Observable<DataStatus<null> | null> = new Observable<null>();
numberOfItems: number = 50;

photoToDelete: AlbumPhotoDelete;
showDeleteConfirmation: boolean;

constructor(private readonly _albumService: AlbumService, windowService: WindowService) {
this._albumService.getAlbums().subscribe(albums => (this.albums = albums));

this.deleteResult$ = this.deleted$.pipe(
switchMap(photoToDelete =>
photoToDelete
? this._albumService.deletePhoto(photoToDelete.albumId, photoToDelete.photoId).pipe(
inspectStatus(),
tap(dataStatus => {
if (dataStatus.status === StateStatus.valid) {
windowService.reload();
}
})
)
: of(null)
)
);
}

onAlbumSelect(): void {
this.selectedAlbumDetails = null;
this._albumService.getById(this.selectedAlbumId).subscribe(albumDetails => (this.selectedAlbumDetails = albumDetails));
this.cancelDelete();
this._albumService
.getById(this.getAlbumId(this.selectedAlbumId))
.subscribe(albumDetails => (this.selectedAlbumDetails = albumDetails));
}

onPendingDelete(photoId: number): void {
const albumId = this.getAlbumId(this.selectedAlbumId);
this.photoToDelete = {
albumId: albumId,
albumName: this.albums.find(album => album.id === albumId)?.title ?? null,
photoId
};
this.showDeleteConfirmation = true;
}

confirmDelete(): void {
this.deleted$.next(this.photoToDelete);
}

cancelDelete(): void {
this.deleted$.next(null);
this.showDeleteConfirmation = false;
}

private getAlbumId(albumId: string): number {
return parseInt(albumId, 10);
}
}

export interface AlbumPhotoDelete {
albumId: number;
albumName: string | null;
photoId: number;
}

This file was deleted.

This file was deleted.

0 comments on commit e77b28b

Please sign in to comment.