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