MME-29 Remove tanstack query stuff from ImageService.
This commit is contained in:
parent
e071b0ed8c
commit
fb4e9f7de1
@ -41,14 +41,14 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (mainImageQuery.isLoading()) {
|
||||
@if (loadingMainImage()) {
|
||||
<app-spinner></app-spinner>
|
||||
} @else if (mainImageQuery.isError()) {
|
||||
} @else if (loadMainImageError()) {
|
||||
<p>There was an error loading the main image.</p>
|
||||
} @else if (mainImageQuery.isEnabled()) {
|
||||
} @else if (mainImage()) {
|
||||
<img
|
||||
id="main-image"
|
||||
[src]="mainImageQuery.data()!.blobUrl"
|
||||
[src]="mainImage()"
|
||||
[alt]="recipe.mainImage!.alt"
|
||||
[height]="recipe.mainImage!.height"
|
||||
[width]="recipe.mainImage!.width"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, computed, inject, input } from '@angular/core';
|
||||
import { Component, computed, inject, input, OnInit, signal } from '@angular/core';
|
||||
import { FullRecipeViewWrapper } from '../../../shared/models/Recipe.model';
|
||||
import { CreateQueryOptions, injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
|
||||
import { injectMutation } from '@tanstack/angular-query-experimental';
|
||||
import { ImageService } from '../../../shared/services/ImageService';
|
||||
import { faEllipsis, faGlobe, faLock, faStar, faUser } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
@ -9,7 +9,6 @@ import { AuthService } from '../../../shared/services/AuthService';
|
||||
import { RecipeCommentsList } from '../../../shared/components/recipe-comments-list/recipe-comments-list';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { Spinner } from '../../../shared/components/spinner/spinner';
|
||||
import { ImageViewWithBlobUrl } from '../../../shared/client-models/ImageViewWithBlobUrl';
|
||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@ -23,7 +22,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||
templateUrl: './recipe-page-content.html',
|
||||
styleUrl: './recipe-page-content.css',
|
||||
})
|
||||
export class RecipePageContent {
|
||||
export class RecipePageContent implements OnInit {
|
||||
public recipeView = input.required<FullRecipeViewWrapper>();
|
||||
|
||||
private readonly imageService = inject(ImageService);
|
||||
@ -34,23 +33,27 @@ export class RecipePageContent {
|
||||
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
||||
protected readonly isOwner = computed(() => !!this.recipeView().isOwner);
|
||||
|
||||
protected readonly mainImageQuery = injectQuery(() => {
|
||||
let options: Partial<
|
||||
CreateQueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]>
|
||||
> = {};
|
||||
const mainImageView = this.recipeView().recipe.mainImage;
|
||||
if (mainImageView) {
|
||||
options = this.imageService.getImage(mainImageView);
|
||||
} else {
|
||||
options.enabled = false;
|
||||
protected readonly loadingMainImage = signal(false);
|
||||
protected readonly loadMainImageError = signal<Error | null>(null);
|
||||
protected readonly mainImage = signal<string | null>(null);
|
||||
|
||||
public ngOnInit(): void {
|
||||
const recipe = this.recipeView().recipe;
|
||||
if (recipe.mainImage) {
|
||||
this.loadingMainImage.set(true);
|
||||
this.imageService.getImageBlobUrl(recipe.mainImage.owner.username, recipe.mainImage.filename).subscribe({
|
||||
next: (blobUrl) => {
|
||||
this.loadingMainImage.set(false);
|
||||
this.mainImage.set(blobUrl);
|
||||
},
|
||||
error: (e) => {
|
||||
this.loadingMainImage.set(false);
|
||||
this.loadMainImageError.set(e);
|
||||
console.error(e);
|
||||
},
|
||||
});
|
||||
}
|
||||
return options as CreateQueryOptions<
|
||||
ImageViewWithBlobUrl,
|
||||
Error,
|
||||
ImageViewWithBlobUrl,
|
||||
[string, string, string]
|
||||
>;
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly starMutation = injectMutation(() => ({
|
||||
mutationFn: () => this.recipeService.toggleStar(this.recipeView()),
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
@let recipe = this.recipe();
|
||||
<article>
|
||||
<a [routerLink]="recipePageLink()">
|
||||
@if (mainImage.isLoading()) {
|
||||
@if (loadingMainImage()) {
|
||||
<app-spinner></app-spinner>
|
||||
} @else if (mainImage.isError()) {
|
||||
<p>There was an error loading the image.</p>
|
||||
} @else if (mainImage.isEnabled()) {
|
||||
<img [src]="mainImage.data()!.blobUrl" id="recipe-card-image" [alt]="recipe.mainImage!.alt" />
|
||||
} @else if (loadMainImageError()) {
|
||||
<p>Error</p>
|
||||
} @else if (mainImage()) {
|
||||
<img [src]="mainImage()" id="recipe-card-image" [alt]="recipe.mainImage!.alt" />
|
||||
} @else {
|
||||
<div class="recipe-card-image-placeholder">
|
||||
<app-logo></app-logo>
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { Component, computed, inject, input } from '@angular/core';
|
||||
import { Component, computed, inject, input, OnInit, signal } from '@angular/core';
|
||||
import { RecipeInfoView } from '../../../models/Recipe.model';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { CreateQueryOptions, injectQuery } from '@tanstack/angular-query-experimental';
|
||||
import { ImageService } from '../../../services/ImageService';
|
||||
import { faGlobe, faLock, faStar, faUser } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { Logo } from '../../logo/logo';
|
||||
import { ImageViewWithBlobUrl } from '../../../client-models/ImageViewWithBlobUrl';
|
||||
import { Spinner } from '../../spinner/spinner';
|
||||
|
||||
@Component({
|
||||
@ -15,7 +13,7 @@ import { Spinner } from '../../spinner/spinner';
|
||||
templateUrl: './recipe-card.html',
|
||||
styleUrl: './recipe-card.css',
|
||||
})
|
||||
export class RecipeCard {
|
||||
export class RecipeCard implements OnInit {
|
||||
public recipe = input.required<RecipeInfoView>();
|
||||
|
||||
protected readonly recipePageLink = computed(() => {
|
||||
@ -30,21 +28,25 @@ export class RecipeCard {
|
||||
|
||||
private readonly imageService = inject(ImageService);
|
||||
|
||||
protected readonly mainImage = injectQuery(() => {
|
||||
const mainImageView = this.recipe().mainImage;
|
||||
let options: Partial<
|
||||
CreateQueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]>
|
||||
> = {};
|
||||
if (mainImageView) {
|
||||
options = this.imageService.getImage(mainImageView);
|
||||
} else {
|
||||
options.enabled = false;
|
||||
protected readonly loadingMainImage = signal(false);
|
||||
protected readonly loadMainImageError = signal<Error | null>(null);
|
||||
protected readonly mainImage = signal<string | null>(null);
|
||||
|
||||
public ngOnInit(): void {
|
||||
const recipe = this.recipe();
|
||||
if (recipe.mainImage) {
|
||||
this.loadingMainImage.set(true);
|
||||
this.imageService.getImageBlobUrl(recipe.mainImage.owner.username, recipe.mainImage.filename).subscribe({
|
||||
next: (blobUrl) => {
|
||||
this.loadingMainImage.set(false);
|
||||
this.mainImage.set(blobUrl);
|
||||
},
|
||||
error: (e) => {
|
||||
this.loadingMainImage.set(false);
|
||||
this.loadMainImageError.set(e);
|
||||
console.error(e);
|
||||
},
|
||||
});
|
||||
}
|
||||
return options as CreateQueryOptions<
|
||||
ImageViewWithBlobUrl,
|
||||
Error,
|
||||
ImageViewWithBlobUrl,
|
||||
[string, string, string]
|
||||
>;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { firstValueFrom, from, map, mergeMap, Observable, tap, toArray } from 'rxjs';
|
||||
import { firstValueFrom, from, map, mergeMap, Observable, shareReplay, toArray } from 'rxjs';
|
||||
import { EndpointService } from './EndpointService';
|
||||
import { ImageView } from '../models/ImageView.model';
|
||||
import { SliceView } from '../models/SliceView.model';
|
||||
import { QueryParams } from '../models/Query.model';
|
||||
import { QueryClient, QueryOptions, queryOptions } from '@tanstack/angular-query-experimental';
|
||||
import { ImageViewWithBlobUrl } from '../client-models/ImageViewWithBlobUrl';
|
||||
import { WithStringDates } from '../util';
|
||||
import { ImageUpdateBody } from '../bodies/ImageUpdateBody';
|
||||
@ -31,7 +30,12 @@ export class ImageService {
|
||||
|
||||
private readonly httpClient = inject(HttpClient);
|
||||
private readonly endpointService = inject(EndpointService);
|
||||
private readonly queryClient = inject(QueryClient);
|
||||
|
||||
private readonly imageBlobUrls: Map<string, Observable<string>> = new Map();
|
||||
|
||||
private getUsernameFilenameKey(username: string, filename: string): string {
|
||||
return username + '/' + filename;
|
||||
}
|
||||
|
||||
public hydrateImageView(rawImageView: WithStringDates<ImageView>): ImageView {
|
||||
return {
|
||||
@ -90,32 +94,23 @@ export class ImageService {
|
||||
.pipe(map((res) => res.count));
|
||||
}
|
||||
|
||||
public getImageViewWithBlobUrl(imageView: ImageView): Promise<ImageViewWithBlobUrl> {
|
||||
return firstValueFrom(
|
||||
this.httpClient
|
||||
.get(this.endpointService.getUrl('images', [imageView.owner.username, imageView.filename]), {
|
||||
responseType: 'blob',
|
||||
})
|
||||
.pipe(
|
||||
map((blob) => URL.createObjectURL(blob)),
|
||||
map(
|
||||
(blobUrl) =>
|
||||
({
|
||||
...imageView,
|
||||
blobUrl,
|
||||
}) satisfies ImageViewWithBlobUrl,
|
||||
),
|
||||
),
|
||||
);
|
||||
private fetchImageViewWithBlobUrl(username: string, filename: string): Observable<string> {
|
||||
return this.httpClient
|
||||
.get(this.endpointService.getUrl('images', [username, filename]), {
|
||||
responseType: 'blob',
|
||||
})
|
||||
.pipe(map((blob) => URL.createObjectURL(blob)));
|
||||
}
|
||||
|
||||
public getImage(
|
||||
imageView: ImageView,
|
||||
): QueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]> {
|
||||
return queryOptions({
|
||||
queryKey: ['image-views-with-blob-urls', imageView.owner.username, imageView.filename],
|
||||
queryFn: () => this.getImageViewWithBlobUrl(imageView),
|
||||
});
|
||||
public getImageBlobUrl(username: string, filename: string): Observable<string> {
|
||||
const key = this.getUsernameFilenameKey(username, filename);
|
||||
if (!this.imageBlobUrls.has(key)) {
|
||||
const blobUrl$ = this.fetchImageViewWithBlobUrl(username, filename).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: false }),
|
||||
);
|
||||
this.imageBlobUrls.set(key, blobUrl$);
|
||||
}
|
||||
return this.imageBlobUrls.get(key)!;
|
||||
}
|
||||
|
||||
public getImageView2(username: string, filename: string): Observable<ImageView> {
|
||||
@ -144,15 +139,7 @@ export class ImageService {
|
||||
formData.append('isPublic', isPublic.toString());
|
||||
}
|
||||
|
||||
return firstValueFrom(
|
||||
this.httpClient.post<ImageView>(this.endpointService.getUrl('images'), formData).pipe(
|
||||
tap(async () => {
|
||||
await this.queryClient.invalidateQueries({
|
||||
queryKey: ['image-views'],
|
||||
});
|
||||
}),
|
||||
),
|
||||
);
|
||||
return firstValueFrom(this.httpClient.post<ImageView>(this.endpointService.getUrl('images'), formData));
|
||||
}
|
||||
|
||||
public imageExists(username: string, filename: string): Promise<boolean> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user