MME-8 WIP refactoring queries for image views, small style. TODO: mutations.
This commit is contained in:
parent
76ed65a144
commit
7c483ba5f6
@ -30,18 +30,19 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (mainImageUrl.isSuccess()) {
|
@if (mainImageQuery.isLoading()) {
|
||||||
@let maybeMainImageUrl = mainImageUrl.data();
|
<app-spinner></app-spinner>
|
||||||
@if (!!maybeMainImageUrl) {
|
} @else if (mainImageQuery.isError()) {
|
||||||
|
<p>There was an error loading the main image.</p>
|
||||||
|
} @else if (mainImageQuery.isEnabled()) {
|
||||||
<img
|
<img
|
||||||
id="main-image"
|
id="main-image"
|
||||||
[src]="maybeMainImageUrl"
|
[src]="mainImageQuery.data()!.blobUrl"
|
||||||
[alt]="recipe.mainImage!.alt"
|
[alt]="recipe.mainImage!.alt"
|
||||||
[height]="recipe.mainImage!.height"
|
[height]="recipe.mainImage!.height"
|
||||||
[width]="recipe.mainImage!.width"
|
[width]="recipe.mainImage!.width"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
<div [innerHTML]="recipe.text"></div>
|
<div [innerHTML]="recipe.text"></div>
|
||||||
<app-recipe-comments-list [recipeUsername]="recipe.owner.username" [recipeSlug]="recipe.slug" />
|
<app-recipe-comments-list [recipeUsername]="recipe.owner.username" [recipeSlug]="recipe.slug" />
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Component, computed, inject, input } from '@angular/core';
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
import { RecipeView } from '../../../shared/models/Recipe.model';
|
import { RecipeView } from '../../../shared/models/Recipe.model';
|
||||||
import { injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
|
import { CreateQueryOptions, injectMutation, injectQuery } from '@tanstack/angular-query-experimental';
|
||||||
import { ImageService } from '../../../shared/services/ImageService';
|
import { ImageService } from '../../../shared/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';
|
||||||
@ -8,10 +8,12 @@ import { RecipeService } from '../../../shared/services/RecipeService';
|
|||||||
import { AuthService } from '../../../shared/services/AuthService';
|
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 { ImageViewWithBlobUrl } from '../../../shared/client-models/ImageViewWithBlobUrl';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recipe-page-content',
|
selector: 'app-recipe-page-content',
|
||||||
imports: [FaIconComponent, RecipeCommentsList, MatButton],
|
imports: [FaIconComponent, RecipeCommentsList, MatButton, Spinner],
|
||||||
templateUrl: './recipe-page-content.html',
|
templateUrl: './recipe-page-content.html',
|
||||||
styleUrl: './recipe-page-content.css',
|
styleUrl: './recipe-page-content.css',
|
||||||
})
|
})
|
||||||
@ -24,12 +26,22 @@ export class RecipePageContent {
|
|||||||
|
|
||||||
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
protected readonly isLoggedIn = computed(() => !!this.authService.accessToken());
|
||||||
|
|
||||||
protected readonly mainImageUrl = injectQuery(() => {
|
protected readonly mainImageQuery = injectQuery(() => {
|
||||||
const recipe = this.recipeView().recipe;
|
let options: Partial<
|
||||||
return {
|
CreateQueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]>
|
||||||
queryKey: ['recipe-main-images', recipe.owner.username, recipe.slug],
|
> = {};
|
||||||
queryFn: () => this.imageService.getImage(recipe.mainImage?.url),
|
const mainImageView = this.recipeView().recipe.mainImage;
|
||||||
};
|
if (mainImageView) {
|
||||||
|
options = this.imageService.getImage(mainImageView);
|
||||||
|
} else {
|
||||||
|
options.enabled = false;
|
||||||
|
}
|
||||||
|
return options as CreateQueryOptions<
|
||||||
|
ImageViewWithBlobUrl,
|
||||||
|
Error,
|
||||||
|
ImageViewWithBlobUrl,
|
||||||
|
[string, string, string]
|
||||||
|
>;
|
||||||
});
|
});
|
||||||
|
|
||||||
protected readonly starMutation = injectMutation(() => ({
|
protected readonly starMutation = injectMutation(() => ({
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, input, OnInit } from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import { DialogContainer } from '../../../../../../shared/components/dialog-container/dialog-container';
|
import { DialogContainer } from '../../../../../../shared/components/dialog-container/dialog-container';
|
||||||
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
@ -6,7 +6,6 @@ import { ImageView } from '../../../../../../shared/models/ImageView.model';
|
|||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { ImageService } from '../../../../../../shared/services/ImageService';
|
import { ImageService } from '../../../../../../shared/services/ImageService';
|
||||||
import { QueryClient } from '@tanstack/angular-query-experimental';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-edit-image-dialog',
|
selector: 'app-edit-image-dialog',
|
||||||
@ -17,7 +16,6 @@ import { QueryClient } from '@tanstack/angular-query-experimental';
|
|||||||
export class EditImageDialog implements OnInit {
|
export class EditImageDialog implements OnInit {
|
||||||
protected readonly imageView: ImageView = inject(MAT_DIALOG_DATA);
|
protected readonly imageView: ImageView = inject(MAT_DIALOG_DATA);
|
||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
private readonly queryClient = inject(QueryClient);
|
|
||||||
private readonly dialogRef = inject(MatDialogRef);
|
private readonly dialogRef = inject(MatDialogRef);
|
||||||
|
|
||||||
protected readonly imageForm = new FormGroup({
|
protected readonly imageForm = new FormGroup({
|
||||||
@ -46,9 +44,6 @@ export class EditImageDialog implements OnInit {
|
|||||||
alt: this.imageForm.value.alt,
|
alt: this.imageForm.value.alt,
|
||||||
caption: this.imageForm.value.caption,
|
caption: this.imageForm.value.caption,
|
||||||
});
|
});
|
||||||
await this.queryClient.invalidateQueries({
|
|
||||||
queryKey: ['image-views'],
|
|
||||||
});
|
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,8 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-card-content p {
|
.image-filename {
|
||||||
|
font-size: 0.75em;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,38 +4,34 @@
|
|||||||
<p>There was an error loading images.</p>
|
<p>There was an error loading images.</p>
|
||||||
} @else if (imageViewsQuery.isSuccess()) {
|
} @else if (imageViewsQuery.isSuccess()) {
|
||||||
<div class="image-grid">
|
<div class="image-grid">
|
||||||
@for (imageQuery of imageQueries(); track $index) {
|
@for (imageQuery of imageViewsWithBlobUrlsQueries(); track $index) {
|
||||||
@if (imageQuery.isLoading()) {
|
@if (imageQuery.isLoading()) {
|
||||||
<app-spinner></app-spinner>
|
<app-spinner></app-spinner>
|
||||||
} @else if (imageQuery.isError()) {
|
} @else if (imageQuery.isError()) {
|
||||||
<p>There was an error loading this image.</p>
|
<p>There was an error loading this image.</p>
|
||||||
} @else {
|
} @else {
|
||||||
@let imageData = imageQuery.data();
|
@let imageView = imageQuery.data()!;
|
||||||
<mat-card>
|
<mat-card>
|
||||||
<img
|
<img
|
||||||
mat-card-image
|
mat-card-image
|
||||||
[src]="imageData!.blobUrl"
|
[src]="imageView.blobUrl"
|
||||||
alt="imageData!.imageView.alt"
|
alt="imageData!.imageView.alt"
|
||||||
(click)="onImageClick(imageData!.imageView)"
|
(click)="onImageClick(imageView)"
|
||||||
class="image-grid-image"
|
class="image-grid-image"
|
||||||
/>
|
/>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<p>{{ imageData!.imageView.filename }}</p>
|
<p class="image-filename">{{ imageView.filename }}</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<mat-checkbox
|
<mat-checkbox [checked]="isSelected(imageView)" (click)="onImageClick(imageView)"
|
||||||
[checked]="isSelected(imageData!.imageView)"
|
|
||||||
(click)="onImageClick(imageData!.imageView)"
|
|
||||||
>Main?</mat-checkbox
|
>Main?</mat-checkbox
|
||||||
>
|
>
|
||||||
<button matButton="text" type="button" [matMenuTriggerFor]="imageActionsMenu">
|
<button matButton="text" type="button" [matMenuTriggerFor]="imageActionsMenu">
|
||||||
<fa-icon [icon]="faEllipsis"></fa-icon>
|
<fa-icon [icon]="faEllipsis"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #imageActionsMenu>
|
<mat-menu #imageActionsMenu>
|
||||||
<button mat-menu-item type="button" (click)="editImage(imageData!.imageView)">Edit</button>
|
<button mat-menu-item type="button" (click)="editImage(imageView)">Edit</button>
|
||||||
<button mat-menu-item type="button" (click)="deleteImage(imageData!.imageView)">
|
<button mat-menu-item type="button" (click)="deleteImage(imageView)">Delete</button>
|
||||||
Delete
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Component, computed, inject, input, output, signal } from '@angular/core';
|
import { Component, computed, inject, input, output, signal } from '@angular/core';
|
||||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import { ImageService } from '../../../../../shared/services/ImageService';
|
import { ImageService } from '../../../../../shared/services/ImageService';
|
||||||
import { injectQuery, keepPreviousData, QueryClient } from '@tanstack/angular-query-experimental';
|
import { injectQuery, keepPreviousData } from '@tanstack/angular-query-experimental';
|
||||||
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
import { Spinner } from '../../../../../shared/components/spinner/spinner';
|
||||||
import { ImageView } from '../../../../../shared/models/ImageView.model';
|
import { ImageView } from '../../../../../shared/models/ImageView.model';
|
||||||
import { injectQueries } from '@tanstack/angular-query-experimental/inject-queries-experimental';
|
import { injectQueries } from '@tanstack/angular-query-experimental/inject-queries-experimental';
|
||||||
@ -42,7 +42,6 @@ export class ImageSelect {
|
|||||||
|
|
||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
private readonly dialog = inject(MatDialog);
|
private readonly dialog = inject(MatDialog);
|
||||||
private readonly queryClient = inject(QueryClient);
|
|
||||||
|
|
||||||
protected readonly imageViewsQuery = injectQuery(() => ({
|
protected readonly imageViewsQuery = injectQuery(() => ({
|
||||||
queryKey: ['image-views', this.currentPage(), this.pageSize()],
|
queryKey: ['image-views', this.currentPage(), this.pageSize()],
|
||||||
@ -68,20 +67,12 @@ export class ImageSelect {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
protected readonly imageQueries = injectQueries(() => ({
|
protected readonly imageViewsWithBlobUrlsQueries = injectQueries(() => ({
|
||||||
queries:
|
queries:
|
||||||
this.imageViewsQuery.data()?.content.map((imageView) => {
|
this.imageViewsQuery.data()?.content.map((imageView) => ({
|
||||||
return {
|
queryKey: ['image-views-with-blob-urls', imageView.owner.username, imageView.filename],
|
||||||
queryKey: ['images', imageView.owner.username, imageView.filename],
|
queryFn: () => this.imageService.getImageViewWithBlobUrl(imageView),
|
||||||
queryFn: async () => {
|
})) ?? [],
|
||||||
const blobUrl = await this.imageService.getImage(imageView.url);
|
|
||||||
return {
|
|
||||||
blobUrl,
|
|
||||||
imageView,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}) ?? [],
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
protected onPage(pageEvent: PageEvent): void {
|
protected onPage(pageEvent: PageEvent): void {
|
||||||
@ -120,12 +111,6 @@ export class ImageSelect {
|
|||||||
|
|
||||||
protected async deleteImage(imageView: ImageView): Promise<void> {
|
protected async deleteImage(imageView: ImageView): Promise<void> {
|
||||||
await this.imageService.deleteImage(imageView.owner.username, imageView.filename);
|
await this.imageService.deleteImage(imageView.owner.username, imageView.filename);
|
||||||
await this.queryClient.invalidateQueries({
|
|
||||||
queryKey: ['image-views'],
|
|
||||||
});
|
|
||||||
await this.queryClient.invalidateQueries({
|
|
||||||
queryKey: ['images'],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly faEllipsis = faEllipsis;
|
protected readonly faEllipsis = faEllipsis;
|
||||||
|
|||||||
5
src/app/shared/client-models/ImageViewWithBlobUrl.ts
Normal file
5
src/app/shared/client-models/ImageViewWithBlobUrl.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { ImageView } from '../models/ImageView.model';
|
||||||
|
|
||||||
|
export interface ImageViewWithBlobUrl extends ImageView {
|
||||||
|
blobUrl: string;
|
||||||
|
}
|
||||||
@ -1,16 +1,17 @@
|
|||||||
@let recipe = this.recipe();
|
@let recipe = this.recipe();
|
||||||
<article>
|
<article>
|
||||||
<a [routerLink]="recipePageLink()">
|
<a [routerLink]="recipePageLink()">
|
||||||
@if (mainImage.isSuccess()) {
|
@if (mainImage.isLoading()) {
|
||||||
@let maybeMainImageUrl = mainImage.data();
|
<app-spinner></app-spinner>
|
||||||
@if (!!maybeMainImageUrl) {
|
} @else if (mainImage.isError()) {
|
||||||
<img [src]="maybeMainImageUrl" id="recipe-card-image" [alt]="recipe.mainImage!.alt" />
|
<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 {
|
} @else {
|
||||||
<div class="recipe-card-image-placeholder">
|
<div class="recipe-card-image-placeholder">
|
||||||
<app-logo></app-logo>
|
<app-logo></app-logo>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</a>
|
</a>
|
||||||
<div id="title-and-visibility">
|
<div id="title-and-visibility">
|
||||||
<a [routerLink]="recipePageLink()">
|
<a [routerLink]="recipePageLink()">
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
import { Component, computed, inject, input } from '@angular/core';
|
import { Component, computed, inject, input } from '@angular/core';
|
||||||
import { Recipe } from '../../../models/Recipe.model';
|
import { Recipe } from '../../../models/Recipe.model';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { injectQuery } from '@tanstack/angular-query-experimental';
|
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';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-recipe-card',
|
selector: 'app-recipe-card',
|
||||||
imports: [RouterLink, FaIconComponent, Logo],
|
imports: [RouterLink, FaIconComponent, Logo, Spinner],
|
||||||
templateUrl: './recipe-card.html',
|
templateUrl: './recipe-card.html',
|
||||||
styleUrl: './recipe-card.css',
|
styleUrl: './recipe-card.css',
|
||||||
})
|
})
|
||||||
@ -29,10 +31,20 @@ export class RecipeCard {
|
|||||||
private readonly imageService = inject(ImageService);
|
private readonly imageService = inject(ImageService);
|
||||||
|
|
||||||
protected readonly mainImage = injectQuery(() => {
|
protected readonly mainImage = injectQuery(() => {
|
||||||
const recipe = this.recipe();
|
const mainImageView = this.recipe().mainImage;
|
||||||
return {
|
let options: Partial<
|
||||||
queryKey: ['recipe-main-images', recipe.owner.username, recipe.slug],
|
CreateQueryOptions<ImageViewWithBlobUrl, Error, ImageViewWithBlobUrl, [string, string, string]>
|
||||||
queryFn: () => this.imageService.getImage(recipe.mainImage?.url),
|
> = {};
|
||||||
};
|
if (mainImageView) {
|
||||||
|
options = this.imageService.getImage(mainImageView);
|
||||||
|
} else {
|
||||||
|
options.enabled = false;
|
||||||
|
}
|
||||||
|
return options as CreateQueryOptions<
|
||||||
|
ImageViewWithBlobUrl,
|
||||||
|
Error,
|
||||||
|
ImageViewWithBlobUrl,
|
||||||
|
[string, string, string]
|
||||||
|
>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
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, map } from 'rxjs';
|
import { firstValueFrom, map, tap } 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';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -27,6 +29,7 @@ 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);
|
||||||
|
|
||||||
public getOwnedImages(queryParams?: QueryParams<typeof ImageService.ImageProps>): Promise<SliceView<ImageView>> {
|
public getOwnedImages(queryParams?: QueryParams<typeof ImageService.ImageProps>): Promise<SliceView<ImageView>> {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
@ -34,21 +37,32 @@ export class ImageService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public getImageViewWithBlobUrl(imageView: ImageView): Promise<ImageViewWithBlobUrl> {
|
||||||
* TODO: this api should not accept null as an input
|
|
||||||
*/
|
|
||||||
public getImage(backendUrl?: string | null): Promise<string | null> {
|
|
||||||
if (!!backendUrl) {
|
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
this.httpClient
|
this.httpClient
|
||||||
.get(backendUrl, {
|
.get(this.endpointService.getUrl('images', [imageView.owner.username, imageView.filename]), {
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
})
|
})
|
||||||
.pipe(map((blob) => URL.createObjectURL(blob))),
|
.pipe(
|
||||||
|
map((blob) => URL.createObjectURL(blob)),
|
||||||
|
map(
|
||||||
|
(blobUrl) =>
|
||||||
|
({
|
||||||
|
...imageView,
|
||||||
|
blobUrl,
|
||||||
|
}) satisfies ImageViewWithBlobUrl,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 uploadImage(
|
public uploadImage(
|
||||||
@ -71,7 +85,15 @@ export class ImageService {
|
|||||||
formData.append('isPublic', isPublic.toString());
|
formData.append('isPublic', isPublic.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return firstValueFrom(this.httpClient.post<ImageView>(this.endpointService.getUrl('images'), formData));
|
return firstValueFrom(
|
||||||
|
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> {
|
||||||
@ -84,7 +106,16 @@ export class ImageService {
|
|||||||
|
|
||||||
public deleteImage(username: string, filename: string): Promise<void> {
|
public deleteImage(username: string, filename: string): Promise<void> {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
this.httpClient.delete<void>(this.endpointService.getUrl('images', [username, filename])),
|
this.httpClient.delete<void>(this.endpointService.getUrl('images', [username, filename])).pipe(
|
||||||
|
tap(async () => {
|
||||||
|
await this.queryClient.refetchQueries({
|
||||||
|
queryKey: ['image-views', username, filename],
|
||||||
|
});
|
||||||
|
await this.queryClient.refetchQueries({
|
||||||
|
queryKey: ['image-views-with-blob-urls', username, filename],
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +132,16 @@ export class ImageService {
|
|||||||
},
|
},
|
||||||
): Promise<ImageView> {
|
): Promise<ImageView> {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
this.httpClient.put<ImageView>(this.endpointService.getUrl('images', [username, filename]), data),
|
this.httpClient.put<ImageView>(this.endpointService.getUrl('images', [username, filename]), data).pipe(
|
||||||
|
tap(async () => {
|
||||||
|
await this.queryClient.refetchQueries({
|
||||||
|
queryKey: ['image-views', username, filename],
|
||||||
|
});
|
||||||
|
await this.queryClient.refetchQueries({
|
||||||
|
queryKey: ['image-views-with-blob-urls', username, filename],
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user