Picsur/frontend/src/app/services/api/image.service.ts
2022-12-27 16:05:53 +01:00

221 lines
5.9 KiB
TypeScript

import { Injectable } from '@angular/core';
import {
ImageDeleteRequest,
ImageDeleteResponse,
ImageListRequest,
ImageListResponse,
ImageUpdateRequest,
ImageUpdateResponse,
ImageUploadResponse,
} from 'picsur-shared/dist/dto/api/image-manage.dto';
import {
ImageMetaResponse,
ImageRequestParams,
} from 'picsur-shared/dist/dto/api/image.dto';
import { ImageLinks } from 'picsur-shared/dist/dto/image-links.class';
import { FileType2Ext } from 'picsur-shared/dist/dto/mimes.dto';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { AsyncFailable } from 'picsur-shared/dist/types';
import {
Fail,
FT,
HasFailed,
HasSuccess,
Open,
} from 'picsur-shared/dist/types/failable';
import { ImagesUploadRequest } from 'src/app/models/dto/images-upload-request.dto';
import { ImageUploadRequest } from '../../models/dto/image-upload-request.dto';
import { ApiService } from './api.service';
import { InfoService } from './info.service';
import { UserService } from './user.service';
@Injectable({
providedIn: 'root',
})
export class ImageService {
constructor(
private readonly api: ApiService,
private readonly infoService: InfoService,
private readonly userService: UserService,
) {}
public async UploadImage(image: File): AsyncFailable<string> {
const result = await this.api.postForm(
ImageUploadResponse,
'/api/image/upload',
new ImageUploadRequest(image),
).result;
return Open(result, 'id');
}
public async UploadImages(images: File[]): AsyncFailable<string[]> {
console.log('Uploading images', images);
// Split into chunks of 20
const groups = this.chunks(images, 20);
const result = await this.api.postForm(
ImageUploadResponse,
'/api/image/upload/bulk',
new ImagesUploadRequest(images),
);
return [];
}
public async GetImageMeta(image: string): AsyncFailable<ImageMetaResponse> {
return await this.api.get(ImageMetaResponse, `/i/meta/${image}`).result;
}
public async ListAllImages(
count: number,
page: number,
userID?: string,
): AsyncFailable<ImageListResponse> {
return await this.api.post(
ImageListRequest,
ImageListResponse,
'/api/image/list',
{
count,
page,
user_id: userID,
},
).result;
}
public async ListMyImages(
count: number,
page: number,
): AsyncFailable<ImageListResponse> {
const userID = await this.userService.snapshot?.id;
if (userID === undefined) {
return Fail(FT.Authentication, 'User not logged in');
}
return await this.ListAllImages(count, page, userID);
}
public async UpdateImage(
id: string,
settings: Partial<Pick<EImage, 'file_name' | 'expires_at'>>,
): AsyncFailable<EImage> {
return await this.api.post(
ImageUpdateRequest,
ImageUpdateResponse,
'/api/image/update',
{
id,
...settings,
},
).result;
}
public async DeleteImages(
images: string[],
): AsyncFailable<ImageDeleteResponse> {
return await this.api.post(
ImageDeleteRequest,
ImageDeleteResponse,
'/api/image/delete',
{
ids: images,
},
).result;
}
public async DeleteImage(image: string): AsyncFailable<EImage> {
const result = await this.DeleteImages([image]);
if (HasFailed(result)) return result;
if (result.images.length !== 1) {
return Fail(
FT.Unknown,
`Image ${image} was not deleted, probably lacking permissions`,
);
}
return result.images[0];
}
// Non api calls
// Use for native images
public GetImageURL(
image: string,
filetype: string | null,
allowOverride = false,
): string {
const baseURL = this.infoService.getHostname(allowOverride);
const extension = FileType2Ext(filetype ?? '');
return `${baseURL}/i/${image}${
HasSuccess(extension) ? '.' + extension : ''
}`;
}
// Use for user facing urls
public CreateImageLinks(imageURL: string, name?: string): ImageLinks {
return {
source: imageURL,
markdown: `![image](${imageURL})`,
html: `<img src="${imageURL}" alt="${name ?? 'image'}">`,
rst: `.. image:: ${imageURL}`,
bbcode: `[img]${imageURL}[/img]`,
};
}
public GetImageURLCustomized(
image: string,
filetype: string | null,
options: ImageRequestParams,
): string {
const baseURL = this.GetImageURL(image, filetype, true);
const betterOptions = ImageRequestParams.zodSchema.safeParse(options);
if (!betterOptions.success) return baseURL;
let queryParams: string[] = [];
if (options.height !== undefined)
queryParams.push(`height=${options.height}`);
if (options.width !== undefined) queryParams.push(`width=${options.width}`);
if (options.rotate !== undefined)
queryParams.push(`rotate=${options.rotate}`);
if (options.flipx !== undefined) queryParams.push(`flipx=${options.flipx}`);
if (options.flipy !== undefined) queryParams.push(`flipy=${options.flipy}`);
if (options.shrinkonly !== undefined)
queryParams.push(`shrinkonly=${options.shrinkonly}`);
if (options.greyscale !== undefined)
queryParams.push(`greyscale=${options.greyscale}`);
if (options.noalpha !== undefined)
queryParams.push(`noalpha=${options.noalpha}`);
if (options.negative !== undefined)
queryParams.push(`negative=${options.negative}`);
if (options.quality !== undefined)
queryParams.push(`quality=${options.quality}`);
if (queryParams.length === 0) return baseURL;
return baseURL + '?' + queryParams.join('&');
}
public CreateImageLinksFromID(
imageID: string,
mime: string | null,
name?: string,
): ImageLinks {
return this.CreateImageLinks(this.GetImageURL(imageID, mime, true), name);
}
private chunks<T>(arr: T[], size: number): T[][] {
let result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, size + i));
}
return result;
}
}