split up utilservice, and change error behaviour

This commit is contained in:
rubikscraft 2022-09-04 19:37:52 +02:00
parent e96c24a669
commit c57ae98f2c
No known key found for this signature in database
GPG Key ID: 1463EBE9200A5CD4
54 changed files with 714 additions and 597 deletions

View File

@ -10,7 +10,7 @@ import {
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { RouteTransitionAnimations } from './app.animation';
import { PRouteData } from './models/dto/picsur-routes.dto';
import { BootstrapService } from './util/util-module/bootstrap.service';
import { BootstrapService } from './util/bootstrap.service';
@Component({
selector: 'app-root',

View File

@ -9,7 +9,9 @@ import { AppRoutingModule } from './app.routing.module';
import { FooterModule } from './components/footer/footer.module';
import { HeaderModule } from './components/header/header.module';
import { GuardsModule } from './guards/guards.module';
import { UtilModule } from './util/util-module/util.module';
import { ApiErrorManagerModule } from './util/api-error-manager/api-error-manager.module';
import { CompatibilityManagerModule } from './util/compatibilitiy-manager/compatibility-manager.module';
import { SnackBarManagerModule } from './util/snackbar-manager/snackbar-manager.module';
@NgModule({
declarations: [AppComponent],
@ -19,7 +21,10 @@ import { UtilModule } from './util/util-module/util.module';
PortalModule,
MatSidenavModule,
UtilModule.forRoot(),
SnackBarManagerModule.forRoot(),
CompatibilityManagerModule,
ApiErrorManagerModule,
GuardsModule,
AppRoutingModule,

View File

@ -1,7 +1,8 @@
import { Clipboard } from '@angular/cdk/clipboard';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Fail, FT } from 'picsur-shared/dist/types';
import { Logger } from 'src/app/services/logger/logger.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
@Component({
selector: 'copy-field',
@ -9,6 +10,8 @@ import { UtilService } from 'src/app/util/util-module/util.service';
styleUrls: ['./copy-field.component.scss'],
})
export class CopyFieldComponent {
private readonly logger = new Logger(CopyFieldComponent.name);
// Two parameters: name, value
@Input() label: string = 'Loading...';
@Input() value: string = 'Loading...';
@ -20,20 +23,20 @@ export class CopyFieldComponent {
@Output('hide') onHide = new EventEmitter<boolean>();
constructor(
private readonly utilService: UtilService,
private readonly clipboard: Clipboard,
private readonly errorService: ErrorService,
) {}
public copy() {
if (this.clipboard.copy(this.value)) {
this.utilService.showSnackBar(`Copied ${this.label}!`, SnackBarType.Info);
this.errorService.info(`Copied ${this.label}!`);
this.onCopy.emit(this.value);
return;
}
return this.utilService.showSnackBar(
'Copying to clipboard failed',
SnackBarType.Error,
return this.errorService.showFailure(
Fail(FT.Internal, 'Copying to clipboard failed'),
this.logger,
);
}

View File

@ -4,11 +4,14 @@ import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { CopyFieldComponent } from './copy-field.component';
@NgModule({
declarations: [CopyFieldComponent],
imports: [
CommonModule,
ErrorManagerModule,
MatInputModule,
MatIconModule,
MatButtonModule,

View File

@ -12,10 +12,10 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { PermissionService } from 'src/app/services/api/permission.service';
import { UserService } from 'src/app/services/api/user.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
@Component({
selector: 'app-header',
@ -24,12 +24,14 @@ import { UtilService } from 'src/app/util/util-module/util.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HeaderComponent implements OnInit {
private readonly logger = new Logger(HeaderComponent.name);
constructor(
private readonly router: Router,
private readonly userService: UserService,
private readonly permissionService: PermissionService,
private readonly utilService: UtilService,
private readonly changeDetector: ChangeDetectorRef,
private readonly errorService: ErrorService
) {}
@Input('enableHamburger') public set enableHamburger(value: boolean) {
@ -90,12 +92,10 @@ export class HeaderComponent implements OnInit {
async doLogout() {
const user = await this.userService.logout();
if (HasFailed(user)) {
this.utilService.showSnackBar(user.getReason(), SnackBarType.Error);
return;
}
if (HasFailed(user))
return this.errorService.showFailure(user, this.logger);
this.utilService.showSnackBar('Logout successful', SnackBarType.Success);
this.errorService.success('Logout successful');
}
doSettings() {

View File

@ -6,11 +6,14 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { HeaderComponent } from './header.component';
@NgModule({
imports: [
CommonModule,
ErrorManagerModule,
MatToolbarModule,
MatButtonModule,
RouterModule,

View File

@ -7,9 +7,9 @@ import {
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { Subject } from 'rxjs';
import { Required } from 'src/app/models/decorators/required.decorator';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { Logger } from 'src/app/services/logger/logger.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { Throttle } from 'src/app/util/throttle';
import { UtilService } from 'src/app/util/util-module/util.service';
@Component({
selector: 'pref-option',
@ -17,6 +17,8 @@ import { UtilService } from 'src/app/util/util-module/util.service';
styleUrls: ['./pref-option.component.scss'],
})
export class PrefOptionComponent implements OnInit {
private readonly logger = new Logger(PrefOptionComponent.name);
@Input() @Required pref: DecodedPref;
@Input('update') @Required updateFunction: (
key: string,
@ -28,7 +30,7 @@ export class PrefOptionComponent implements OnInit {
private updateSubject = new Subject<PrefValueType>();
constructor(private readonly utilService: UtilService) {}
constructor(private readonly errorService: ErrorService) {}
ngOnInit(): void {
this.subscribeUpdate();
@ -87,12 +89,9 @@ export class PrefOptionComponent implements OnInit {
? `Enabled ${this.name}`
: `Disabled ${this.name}`
: '';
this.utilService.showSnackBar(message, SnackBarType.Success);
this.errorService.success(message);
} else {
this.utilService.showSnackBar(
`Failed to update ${this.name}`,
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
}
}

View File

@ -3,11 +3,14 @@ import { NgModule } from '@angular/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { PrefOptionComponent } from './pref-option.component';
@NgModule({
imports: [
CommonModule,
ErrorManagerModule,
MatFormFieldModule,
MatInputModule,
MatSlideToggleModule,

View File

@ -4,14 +4,11 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { ImageFileType } from 'picsur-shared/dist/dto/mimes.dto';
import { EImage } from 'picsur-shared/dist/entities/image.entity';
import { HasFailed } from 'picsur-shared/dist/types/failable';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ImageService } from 'src/app/services/api/image.service';
import { Logger } from 'src/app/services/logger/logger.service';
import {
BootstrapService,
BSScreenSize
} from 'src/app/util/util-module/bootstrap.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { BootstrapService, BSScreenSize } from 'src/app/util/bootstrap.service';
import { DialogService } from 'src/app/util/dialog-manager/dialog.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
@Component({
templateUrl: './images.component.html',
@ -30,8 +27,9 @@ export class ImagesComponent implements OnInit {
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly bootstrapService: BootstrapService,
private readonly utilService: UtilService,
private readonly imageService: ImageService,
private readonly errorService: ErrorService,
private readonly dialogService: DialogService,
) {}
ngOnInit() {
@ -71,7 +69,8 @@ export class ImagesComponent implements OnInit {
getThumbnailUrl(image: EImage) {
return (
this.imageService.GetImageURL(image.id, ImageFileType.QOI) + '?height=480&shrinkonly=yes'
this.imageService.GetImageURL(image.id, ImageFileType.QOI) +
'?height=480&shrinkonly=yes'
);
}
@ -80,7 +79,7 @@ export class ImagesComponent implements OnInit {
}
async deleteImage(image: EImage) {
const pressedButton = await this.utilService.showDialog({
const pressedButton = await this.dialogService.showDialog({
title: `Are you sure you want to delete the image?`,
description: 'This action cannot be undone.',
buttons: [
@ -98,15 +97,11 @@ export class ImagesComponent implements OnInit {
if (pressedButton === 'delete') {
const result = await this.imageService.DeleteImage(image.id ?? '');
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete image',
SnackBarType.Error,
);
} else {
this.utilService.showSnackBar('Image deleted', SnackBarType.Success);
this.images = this.images?.filter((i) => i.id !== image.id) ?? null;
}
if (HasFailed(result))
return this.errorService.showFailure(result, this.logger);
this.errorService.success('Image deleted');
this.images = this.images?.filter((i) => i.id !== image.id) ?? null;
}
}

View File

@ -8,6 +8,8 @@ import { MasonryModule } from 'src/app/components/masonry/masonry.module';
import { PaginatorModule } from 'src/app/components/paginator/paginator.module';
import { PicsurImgModule } from 'src/app/components/picsur-img/picsur-img.module';
import { PipesModule } from 'src/app/pipes/pipes.module';
import { DialogManagerModule } from 'src/app/util/dialog-manager/dialog-manager.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { ImagesComponent } from './images.component';
import { ImagesRoutingModule } from './images.routing.module';
@ -15,6 +17,9 @@ import { ImagesRoutingModule } from './images.routing.module';
declarations: [ImagesComponent],
imports: [
CommonModule,
ErrorManagerModule,
DialogManagerModule,
ImagesRoutingModule,
MatCardModule,
MatButtonModule,
@ -23,7 +28,7 @@ import { ImagesRoutingModule } from './images.routing.module';
PaginatorModule,
PicsurImgModule,
MomentModule,
PipesModule
PipesModule,
],
})
export class ImagesRouteModule {}

View File

@ -1,32 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { HasFailed } from 'picsur-shared/dist/types';
import { Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { ProcessingViewMeta } from 'src/app/models/dto/processing-view-meta.dto';
import { ImageService } from 'src/app/services/api/image.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
@Component({
templateUrl: './processing.component.html',
})
export class ProcessingComponent implements OnInit {
private readonly logger = new Logger(ProcessingComponent.name);
constructor(
private readonly router: Router,
private readonly imageService: ImageService,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {}
async ngOnInit() {
const state = history.state as ProcessingViewMeta;
if (!state) {
return this.utilService.quitError('Error');
return this.errorService.quitFailure(
Fail(FT.UsrValidation, 'No state provided'),
this.logger,
);
}
history.replaceState(null, '');
const id = await this.imageService.UploadImage(state.imageFile);
if (HasFailed(id)) {
return this.utilService.quitError(id.getReason());
}
if (HasFailed(id))
return this.errorService.quitFailure(id, this.logger);
this.router.navigate([`/view/`, id], { replaceUrl: true });
}

View File

@ -1,11 +1,17 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { ProcessingComponent } from './processing.component';
import { ProcessingRoutingModule } from './processing.routing.module';
@NgModule({
declarations: [ProcessingComponent],
imports: [CommonModule, ProcessingRoutingModule, MatProgressSpinnerModule],
imports: [
CommonModule,
ErrorManagerModule,
ProcessingRoutingModule,
MatProgressSpinnerModule,
],
})
export class ProcessingRouteModule {}

View File

@ -3,15 +3,15 @@ import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { EApiKey } from 'picsur-shared/dist/entities/apikey.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject, Subject } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ApiKeysService } from 'src/app/services/api/apikeys.service';
import { UserService } from 'src/app/services/api/user.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { BootstrapService } from 'src/app/util/bootstrap.service';
import { DialogService } from 'src/app/util/dialog-manager/dialog.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { Throttle } from 'src/app/util/throttle';
import { BootstrapService } from 'src/app/util/util-module/bootstrap.service';
import { UtilService } from 'src/app/util/util-module/util.service';
@Component({
templateUrl: './settings-apikeys.component.html',
@ -36,10 +36,11 @@ export class SettingsApiKeysComponent implements OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor(
private readonly utilService: UtilService,
private readonly apikeysService: ApiKeysService,
private readonly userService: UserService,
private readonly clipboard: Clipboard,
private readonly errorService: ErrorService,
private readonly dialogService: DialogService,
// Public because used in template
public readonly bootstrapService: BootstrapService,
) {}
@ -52,13 +53,8 @@ export class SettingsApiKeysComponent implements OnInit {
public async addApiKey() {
const result = await this.apikeysService.createApiKey();
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to create api key',
SnackBarType.Error,
);
return;
}
if (HasFailed(result))
return this.errorService.showFailure(result, this.logger);
const success = await this.fetchApiKeys(
this.paginator.pageSize,
@ -70,35 +66,29 @@ export class SettingsApiKeysComponent implements OnInit {
const clipboardResult = this.clipboard.copy(result.key);
if (!clipboardResult) {
this.utilService.showSnackBar(
'Failed to copy api key to clipboard',
SnackBarType.Error,
return this.errorService.showFailure(
Fail(FT.Internal, 'Failed to copy api key to clipboard'),
this.logger,
);
}
this.utilService.showSnackBar(
'Api key created and copied to clipboard',
SnackBarType.Success,
);
this.errorService.success('Api key created and copied to clipboard');
}
public copyKey(apikey: string) {
const result = this.clipboard.copy(apikey);
if (!result) {
this.utilService.showSnackBar(
'Failed to copy api key to clipboard',
SnackBarType.Error,
);
} else {
this.utilService.showSnackBar(
'Api key copied to clipboard',
SnackBarType.Success,
return this.errorService.showFailure(
Fail(FT.Internal, 'Failed to copy api key to clipboard'),
this.logger,
);
}
this.errorService.success('Api key copied to clipboard');
}
public async deleteApiKey(apikeyId: string) {
const pressedButton = await this.utilService.showDialog({
const pressedButton = await this.dialogService.showDialog({
title: `Are you sure you want to delete this api key?`,
description: 'This action cannot be undone.',
buttons: [
@ -117,12 +107,9 @@ export class SettingsApiKeysComponent implements OnInit {
if (pressedButton === 'delete') {
const result = await this.apikeysService.deleteApiKey(apikeyId);
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete api key',
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
} else {
this.utilService.showSnackBar('Api key deleted', SnackBarType.Success);
this.errorService.success('Api key deleted');
}
}
@ -138,18 +125,10 @@ export class SettingsApiKeysComponent implements OnInit {
async updateKeyName(name: string, apikeyID: string) {
const result = await this.apikeysService.updateApiKey(apikeyID, name);
if (HasFailed(result)) {
this.logger.warn(result.print());
this.utilService.showSnackBar(
'Failed to update api key name',
SnackBarType.Error,
);
} else {
this.utilService.showSnackBar(
'Api key name updated',
SnackBarType.Success,
);
}
if (HasFailed(result))
return this.errorService.showFailure(result, this.logger);
this.errorService.success('Api key name updated');
}
@AutoUnsubscribe()
@ -180,13 +159,9 @@ export class SettingsApiKeysComponent implements OnInit {
pageIndex,
this.userService.snapshot?.id,
);
if (HasFailed(response)) {
this.utilService.showSnackBar(
'Failed to fetch api keys',
SnackBarType.Error,
);
this.logger.warn(response.print());
return false;
if (HasFailed(response)){
this.errorService.showFailure(response, this.logger);
return false;
}
this.dataSubject.next(response.results);

View File

@ -9,6 +9,8 @@ import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { MomentModule } from 'ngx-moment';
import { FabModule } from 'src/app/components/fab/fab.module';
import { DialogManagerModule } from 'src/app/util/dialog-manager/dialog-manager.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { SettingsApiKeyEditorComponent } from './apikey-editor/apikey-editor.component';
import { SettingsApiKeysComponent } from './settings-apikeys.component';
import { SettingsApiKeysRoutingModule } from './settings-apikeys.routing.module';
@ -17,6 +19,9 @@ import { SettingsApiKeysRoutingModule } from './settings-apikeys.routing.module'
declarations: [SettingsApiKeysComponent, SettingsApiKeyEditorComponent],
imports: [
CommonModule,
ErrorManagerModule,
DialogManagerModule,
SettingsApiKeysRoutingModule,
MatButtonModule,
MatIconModule,

View File

@ -3,12 +3,11 @@ import { ActivatedRoute, Router } from '@angular/router';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { HasFailed } from 'picsur-shared/dist/types';
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UpdateRoleControl } from 'src/app/models/forms/update-role.control';
import { RolesService } from 'src/app/services/api/roles.service';
import { StaticInfoService } from 'src/app/services/api/static-info.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
enum EditMode {
edit = 'edit',
@ -37,9 +36,9 @@ export class SettingsRolesEditComponent implements OnInit {
constructor(
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly utilService: UtilService,
private readonly rolesService: RolesService,
private readonly staticInfo: StaticInfoService,
private readonly errorService: ErrorService,
) {}
ngOnInit() {
@ -60,10 +59,8 @@ export class SettingsRolesEditComponent implements OnInit {
// Fetch data and populate form
const role = await this.rolesService.getRole(rolename);
if (HasFailed(role)) {
this.utilService.showSnackBar('Failed to get role', SnackBarType.Error);
return;
}
if (HasFailed(role))
return this.errorService.showFailure(role, this.logger);
this.model.putRoleName(role.name);
this.model.putPermissions(role.permissions);
}
@ -78,26 +75,16 @@ export class SettingsRolesEditComponent implements OnInit {
if (this.adding) {
const resultRole = await this.rolesService.createRole(data);
if (HasFailed(resultRole)) {
this.utilService.showSnackBar(
'Failed to create role',
SnackBarType.Error,
);
return;
}
if (HasFailed(resultRole))
return this.errorService.showFailure(resultRole, this.logger);
this.utilService.showSnackBar('Role created', SnackBarType.Success);
this.errorService.success('Role created');
} else {
const resultRole = await this.rolesService.updateRole(data);
if (HasFailed(resultRole)) {
this.utilService.showSnackBar(
'Failed to update role',
SnackBarType.Error,
);
return;
}
if (HasFailed(resultRole))
return this.errorService.showFailure(resultRole, this.logger);
this.utilService.showSnackBar('Role updated', SnackBarType.Success);
this.errorService.success('Role updated');
}
this.router.navigate(['/settings/roles']);

View File

@ -6,12 +6,12 @@ import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { ERole } from 'picsur-shared/dist/entities/role.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { RolesService } from 'src/app/services/api/roles.service';
import { StaticInfoService } from 'src/app/services/api/static-info.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { BootstrapService } from 'src/app/util/util-module/bootstrap.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { BootstrapService } from 'src/app/util/bootstrap.service';
import { DialogService } from 'src/app/util/dialog-manager/dialog.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
@Component({
templateUrl: './settings-roles.component.html',
@ -37,10 +37,11 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor(
private readonly utilService: UtilService,
private readonly rolesService: RolesService,
private readonly staticInfo: StaticInfoService,
private readonly router: Router,
private readonly errorService: ErrorService,
private readonly dialogService: DialogService,
// Public because used in template
public readonly bootstrapService: BootstrapService,
) {}
@ -62,7 +63,7 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
}
async deleteRole(role: ERole) {
const pressedButton = await this.utilService.showDialog({
const pressedButton = await this.dialogService.showDialog({
title: `Are you sure you want to delete ${role.name}?`,
description: 'This action cannot be undone.',
buttons: [
@ -81,12 +82,9 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
if (pressedButton === 'delete') {
const result = await this.rolesService.deleteRole(role.name);
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete role',
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
} else {
this.utilService.showSnackBar('Role deleted', SnackBarType.Success);
this.errorService.success('Role deleted');
}
}
@ -113,10 +111,8 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
this.UndeletableRolesList = specialRoles.UndeletableRoles;
this.ImmutableRolesList = specialRoles.ImmutableRoles;
if (HasFailed(roles)) {
this.utilService.showSnackBar('Failed to load roles', SnackBarType.Error);
return;
}
if (HasFailed(roles))
return this.errorService.showFailure(roles, this.logger);
this.dataSource.data = roles;
}
}

View File

@ -10,6 +10,8 @@ import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { FabModule } from 'src/app/components/fab/fab.module';
import { ValuesPickerModule } from 'src/app/components/values-picker/values-picker.module';
import { DialogManagerModule } from 'src/app/util/dialog-manager/dialog-manager.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { SettingsRolesEditComponent } from './settings-roles-edit/settings-roles-edit.component';
import { SettingsRolesComponent } from './settings-roles.component';
import { SettingsRolesRoutingModule } from './settings-roles.routing.module';
@ -18,6 +20,9 @@ import { SettingsRolesRoutingModule } from './settings-roles.routing.module';
declarations: [SettingsRolesComponent, SettingsRolesEditComponent],
imports: [
CommonModule,
ErrorManagerModule,
DialogManagerModule,
SettingsRolesRoutingModule,
MatIconModule,
MatButtonModule,

View File

@ -6,12 +6,11 @@ import { EApiKey } from 'picsur-shared/dist/entities/apikey.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject } from 'rxjs';
import { scan } from 'rxjs/operators';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ApiKeysService } from 'src/app/services/api/apikeys.service';
import { PermissionService } from 'src/app/services/api/permission.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { SimpleUtilService } from 'src/app/util/util-module/simple-util.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { UtilService } from 'src/app/util/util.service';
import { BuildShareX } from './sharex-builder';
@Component({
@ -41,13 +40,13 @@ export class SettingsShareXComponent implements OnInit {
constructor(
private readonly apikeysService: ApiKeysService,
private readonly utilService: UtilService,
private readonly simpleUtil: SimpleUtilService,
private readonly permissionService: PermissionService,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {}
ngOnInit(): void {
this.formatOptions = this.simpleUtil.getBaseFormatOptions();
this.formatOptions = this.utilService.getBaseFormatOptions();
this.getNextBatch();
}
@ -67,22 +66,19 @@ export class SettingsShareXComponent implements OnInit {
}
const sharexConfig = BuildShareX(
this.simpleUtil.getHost(),
this.utilService.getHost(),
this.key,
'.' + ext,
canUseDelete,
);
this.simpleUtil.downloadBuffer(
this.utilService.downloadBuffer(
JSON.stringify(sharexConfig),
'Pisur-ShareX-target.sxcu',
'application/json',
);
this.utilService.showSnackBar(
'Exported ShareX config',
SnackBarType.Success,
);
this.errorService.success('Exported ShareX config');
}
async getNextBatch() {
@ -90,10 +86,7 @@ export class SettingsShareXComponent implements OnInit {
50,
Math.floor(this.loaded / 50),
);
if (HasFailed(newApiKeys)) {
this.utilService.showSnackBar(newApiKeys.getReason(), SnackBarType.Error);
return;
}
if (HasFailed(newApiKeys)) return this.errorService.showFailure(newApiKeys, this.logger);
this.loaded += newApiKeys.results.length;
this.available = newApiKeys.total;

View File

@ -5,7 +5,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatSelectInfiniteScrollModule } from 'ng-mat-select-infinite-scroll';
import { UtilModule } from 'src/app/util/util-module/util.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { SettingsShareXComponent } from './settings-sharex.component';
import { SettingsShareXRoutingModule } from './settings-sharex.routing.module';
@ -13,13 +13,14 @@ import { SettingsShareXRoutingModule } from './settings-sharex.routing.module';
declarations: [SettingsShareXComponent],
imports: [
CommonModule,
ErrorManagerModule,
SettingsShareXRoutingModule,
MatSelectModule,
MatSelectInfiniteScrollModule,
MatInputModule,
MatButtonModule,
MatProgressSpinnerModule,
UtilModule,
],
})
export class SettingsShareXRouteModule {}

View File

@ -4,13 +4,12 @@ import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { ERole } from 'picsur-shared/dist/entities/role.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UpdateUserControl } from 'src/app/models/forms/update-user.control';
import { RolesService } from 'src/app/services/api/roles.service';
import { StaticInfoService } from 'src/app/services/api/static-info.service';
import { UserAdminService } from 'src/app/services/api/user-manage.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
enum EditMode {
edit = 'edit',
@ -46,9 +45,9 @@ export class SettingsUsersEditComponent implements OnInit {
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly userManageService: UserAdminService,
private readonly utilService: UtilService,
private readonly rolesService: RolesService,
private readonly staticInfo: StaticInfoService,
private readonly errorService: ErrorService,
) {}
ngOnInit() {
@ -80,10 +79,8 @@ export class SettingsUsersEditComponent implements OnInit {
// Fetch more data
const user = await this.userManageService.getUser(uuid);
if (HasFailed(user)) {
this.utilService.showSnackBar('Failed to get user', SnackBarType.Error);
return;
}
if (HasFailed(user))
return this.errorService.showFailure(user, this.logger);
// Set that data instead
this.model.putUsername(user.username);
@ -97,10 +94,8 @@ export class SettingsUsersEditComponent implements OnInit {
private async initRoles() {
const roles = await this.rolesService.getRoles();
if (HasFailed(roles)) {
this.utilService.showSnackBar('Failed to get roles', SnackBarType.Error);
return;
}
if (HasFailed(roles))
return this.errorService.showFailure(roles, this.logger);
this.allFullRoles = roles;
}
@ -131,29 +126,19 @@ export class SettingsUsersEditComponent implements OnInit {
if (this.adding) {
const data = this.model.getDataCreate();
const resultUser = await this.userManageService.createUser(data);
if (HasFailed(resultUser)) {
this.utilService.showSnackBar(
'Failed to create user',
SnackBarType.Error,
);
return;
}
if (HasFailed(resultUser))
return this.errorService.showFailure(resultUser, this.logger);
this.utilService.showSnackBar('User created', SnackBarType.Success);
this.errorService.success('User created');
} else {
const data = this.model.getDataUpdate();
if (!data.password) delete data.password;
const resultUser = await this.userManageService.updateUser(data);
if (HasFailed(resultUser)) {
this.utilService.showSnackBar(
'Failed to update user',
SnackBarType.Error,
);
return;
}
if (HasFailed(resultUser))
return this.errorService.showFailure(resultUser, this.logger);
this.utilService.showSnackBar('User updated', SnackBarType.Success);
this.errorService.success('User updated');
}
this.router.navigate(['/settings/users']);

View File

@ -5,13 +5,13 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject, Subject } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { StaticInfoService } from 'src/app/services/api/static-info.service';
import { UserAdminService } from 'src/app/services/api/user-manage.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { BootstrapService } from 'src/app/util/bootstrap.service';
import { DialogService } from 'src/app/util/dialog-manager/dialog.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { Throttle } from 'src/app/util/throttle';
import { BootstrapService } from 'src/app/util/util-module/bootstrap.service';
import { UtilService } from 'src/app/util/util-module/util.service';
@Component({
templateUrl: './settings-users.component.html',
@ -34,10 +34,11 @@ export class SettingsUsersComponent implements OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
constructor(
private readonly utilService: UtilService,
private readonly userManageService: UserAdminService,
private readonly staticInfo: StaticInfoService,
private readonly router: Router,
private readonly errorService: ErrorService,
private readonly dialogService: DialogService,
// Public because used in template
public readonly bootstrapService: BootstrapService,
) {}
@ -60,7 +61,7 @@ export class SettingsUsersComponent implements OnInit {
}
public async deleteUser(user: EUser) {
const pressedButton = await this.utilService.showDialog({
const pressedButton = await this.dialogService.showDialog({
title: `Are you sure you want to delete ${user.username}?`,
description: 'This action cannot be undone.',
buttons: [
@ -79,12 +80,9 @@ export class SettingsUsersComponent implements OnInit {
if (pressedButton === 'delete') {
const result = await this.userManageService.deleteUser(user.id ?? '');
if (HasFailed(result)) {
this.utilService.showSnackBar(
'Failed to delete user',
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
} else {
this.utilService.showSnackBar('User deleted', SnackBarType.Success);
this.errorService.success('User deleted');
}
}
@ -122,10 +120,7 @@ export class SettingsUsersComponent implements OnInit {
): Promise<boolean> {
const response = await this.userManageService.getUsers(pageSize, pageIndex);
if (HasFailed(response)) {
this.utilService.showSnackBar(
'Failed to fetch users',
SnackBarType.Error,
);
this.errorService.showFailure(response, this.logger);
return false;
}

View File

@ -10,6 +10,8 @@ import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table';
import { FabModule } from 'src/app/components/fab/fab.module';
import { ValuesPickerModule } from 'src/app/components/values-picker/values-picker.module';
import { DialogManagerModule } from 'src/app/util/dialog-manager/dialog-manager.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { SettingsUsersEditComponent } from './settings-users-edit/settings-users-edit.component';
import { SettingsUsersComponent } from './settings-users.component';
import { SettingsUsersRoutingModule } from './settings-users.routing.module';
@ -18,6 +20,9 @@ import { SettingsUsersRoutingModule } from './settings-users.routing.module';
declarations: [SettingsUsersComponent, SettingsUsersEditComponent],
imports: [
CommonModule,
ErrorManagerModule,
DialogManagerModule,
SettingsUsersRoutingModule,
MatButtonModule,
MatIconModule,

View File

@ -3,10 +3,11 @@ import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { NgxDropzoneChangeEvent } from 'ngx-dropzone';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { Fail, FT } from 'picsur-shared/dist/types';
import { debounceTime } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { PermissionService } from 'src/app/services/api/permission.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { ProcessingViewMeta } from '../../models/dto/processing-view-meta.dto';
@Component({
@ -14,12 +15,14 @@ import { ProcessingViewMeta } from '../../models/dto/processing-view-meta.dto';
styleUrls: ['./upload.component.scss'],
})
export class UploadComponent implements OnInit {
private readonly logger = new Logger(UploadComponent.name);
canUpload = true;
constructor(
private readonly utilService: UtilService,
private readonly permissionService: PermissionService,
private readonly router: Router,
private readonly permissionService: PermissionService,
private readonly errorService: ErrorService,
) {}
ngOnInit(): void {
@ -36,11 +39,10 @@ export class UploadComponent implements OnInit {
}
onSelect(event: NgxDropzoneChangeEvent) {
if (event.addedFiles.length > 1) {
this.utilService.showSnackBar(
if (event.addedFiles.length > 1)
this.errorService.log(
'You uploaded multiple images, only one has been uploaded',
);
}
const metadata: ProcessingViewMeta = {
imageFile: event.addedFiles[0],
@ -51,36 +53,28 @@ export class UploadComponent implements OnInit {
@HostListener('document:paste', ['$event'])
onPaste(event: ClipboardEvent) {
const items = event.clipboardData?.items;
if (!items) {
this.utilService.showSnackBar('Your clipboard is empty');
return;
}
if (!items) return this.errorService.info('Your clipboard is empty');
const filteredItems = Array.from(items).filter(
(item) => item.kind === 'file',
);
if (filteredItems.length === 0) {
this.utilService.showSnackBar(
if (filteredItems.length === 0)
return this.errorService.info(
'Your clipboard does not contain any images',
);
return;
}
const blob = filteredItems[0].getAsFile();
if (!blob) {
this.utilService.showSnackBar(
'Error getting image from clipboard',
SnackBarType.Error,
if (!blob)
return this.errorService.showFailure(
Fail(FT.Internal, 'Error getting image from clipboard'),
this.logger,
);
return;
}
if (filteredItems.length > 1) {
this.utilService.showSnackBar(
if (filteredItems.length > 1)
this.errorService.log(
'You pasted multiple images, only one has been uploaded',
);
}
const metadata: ProcessingViewMeta = {
imageFile: blob,

View File

@ -1,11 +1,17 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { NgxDropzoneModule } from 'ngx-dropzone';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { UploadComponent } from './upload.component';
import { UploadRoutingModule } from './upload.routing.module';
@NgModule({
declarations: [UploadComponent],
imports: [CommonModule, UploadRoutingModule, NgxDropzoneModule],
imports: [
CommonModule,
ErrorManagerModule,
UploadRoutingModule,
NgxDropzoneModule,
],
})
export class UploadRouteModule {}

View File

@ -3,12 +3,11 @@ import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { HasFailed } from 'picsur-shared/dist/types';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UserPassModel } from 'src/app/models/forms-dto/userpass.dto';
import { PermissionService } from 'src/app/services/api/permission.service';
import { UserService } from 'src/app/services/api/user.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { LoginControl } from '../../../models/forms/login.control';
@Component({
@ -27,7 +26,7 @@ export class LoginComponent implements OnInit {
private readonly userService: UserService,
private readonly permissionService: PermissionService,
private readonly router: Router,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {}
ngOnInit(): void {
@ -57,16 +56,10 @@ export class LoginComponent implements OnInit {
const user = await this.userService.login(data.username, data.password);
this.loading = false;
if (HasFailed(user)) {
this.logger.error(user.getReason());
this.utilService.showSnackBar(
'Login failed, please try again',
SnackBarType.Error,
);
return;
}
if (HasFailed(user))
return this.errorService.showFailure(user, this.logger);
this.utilService.showSnackBar('Login successful', SnackBarType.Success);
this.errorService.success('Login successful');
this.router.navigate(['/']);
}

View File

@ -3,12 +3,11 @@ import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { HasFailed } from 'picsur-shared/dist/types';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UserPassModel } from 'src/app/models/forms-dto/userpass.dto';
import { PermissionService } from 'src/app/services/api/permission.service';
import { UserService } from 'src/app/services/api/user.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { RegisterControl } from '../../../models/forms/register.control';
@Component({
@ -27,7 +26,7 @@ export class RegisterComponent implements OnInit {
private readonly userService: UserService,
private readonly permissionService: PermissionService,
private readonly router: Router,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {}
ngOnInit(): void {
@ -58,12 +57,7 @@ export class RegisterComponent implements OnInit {
if (HasFailed(user)) {
this.loading = false;
this.logger.error(user.getReason());
this.utilService.showSnackBar(
'Register failed, please try again',
SnackBarType.Error,
);
return;
return this.errorService.showFailure(user, this.logger);
}
if (!this.userService.isLoggedIn) {
@ -72,22 +66,14 @@ export class RegisterComponent implements OnInit {
data.password,
);
if (HasFailed(loginResult)) {
this.logger.error(loginResult.getReason());
this.utilService.showSnackBar(
'Failed to login after register',
SnackBarType.Error,
);
this.loading = false;
return this.errorService.showFailure(loginResult, this.logger);
}
this.utilService.showSnackBar(
'Register successful',
SnackBarType.Success,
);
this.errorService.success('Register successful');
} else {
this.utilService.showSnackBar(
'Register successful, did not log in',
SnackBarType.Success,
);
this.errorService.success('Register successful, did not log in');
}
this.loading = false;

View File

@ -4,6 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { UserRoutingModule } from './user.routing.module';
@ -12,6 +13,8 @@ import { UserRoutingModule } from './user.routing.module';
declarations: [LoginComponent, RegisterComponent],
imports: [
CommonModule,
ErrorManagerModule,
UserRoutingModule,
FormsModule,
MatInputModule,

View File

@ -4,7 +4,8 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { ImageLinks } from 'picsur-shared/dist/dto/image-links.class';
import {
AnimFileType,
FileType, ImageFileType,
FileType,
ImageFileType,
SupportedFileTypeCategory
} from 'picsur-shared/dist/dto/mimes.dto';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
@ -14,12 +15,15 @@ import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed, HasSuccess } from 'picsur-shared/dist/types';
import { UUIDRegex } from 'picsur-shared/dist/util/common-regex';
import { ParseFileType } from 'picsur-shared/dist/util/parse-mime';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ImageService } from 'src/app/services/api/image.service';
import { PermissionService } from 'src/app/services/api/permission.service';
import { UserService } from 'src/app/services/api/user.service';
import { SimpleUtilService } from 'src/app/util/util-module/simple-util.service';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { DialogService } from 'src/app/util/dialog-manager/dialog.service';
import { DownloadService } from 'src/app/util/download-manager/download.service';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { UtilService } from 'src/app/util/util.service';
import {
CustomizeDialogComponent,
CustomizeDialogData
@ -30,14 +34,19 @@ import {
styleUrls: ['./view.component.scss'],
})
export class ViewComponent implements OnInit {
private readonly logger = new Logger(ViewComponent.name);
constructor(
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly imageService: ImageService,
private readonly utilService: UtilService,
private readonly simpleUtil: SimpleUtilService,
private readonly permissionService: PermissionService,
private readonly userService: UserService,
private readonly errorService: ErrorService,
private readonly downloadService: DownloadService,
private readonly dialogService: DialogService,
private readonly utilService: UtilService,
) {}
private id: string;
@ -71,13 +80,13 @@ export class ViewComponent implements OnInit {
this.id = params.get('id') ?? '';
if (!UUIDRegex.test(this.id)) {
return this.utilService.quitError('Invalid image link');
return this.errorService.quitError('Invalid image link', this.logger);
}
// Get metadata
const metadata = await this.imageService.GetImageMeta(this.id);
if (HasFailed(metadata))
return this.utilService.quitError(metadata.getReason());
return this.errorService.quitFailure(metadata, this.logger);
// Get width of screen in pixels
const width = window.innerWidth * window.devicePixelRatio;
@ -126,11 +135,11 @@ export class ViewComponent implements OnInit {
}
download() {
this.utilService.downloadFile(this.imageLinks.source);
this.downloadService.downloadFile(this.imageLinks.source);
}
share() {
this.utilService.shareFile(this.imageLinks.source);
this.downloadService.shareFile(this.imageLinks.source);
}
goBackHome() {
@ -138,7 +147,7 @@ export class ViewComponent implements OnInit {
}
async deleteImage() {
const pressedButton = await this.utilService.showDialog({
const pressedButton = await this.dialogService.showDialog({
title: `Are you sure you want to delete the image?`,
description: 'This action cannot be undone.',
buttons: [
@ -156,14 +165,10 @@ export class ViewComponent implements OnInit {
if (pressedButton === 'delete') {
const result = await this.imageService.DeleteImage(this.id);
if (HasFailed(result)) {
return this.utilService.showSnackBar(
'Failed to delete image',
SnackBarType.Error,
);
}
if (HasFailed(result))
return this.errorService.showFailure(result, this.logger);
this.utilService.showSnackBar('Image deleted', SnackBarType.Success);
this.errorService.success('Image deleted');
this.router.navigate(['/']);
}
@ -173,16 +178,20 @@ export class ViewComponent implements OnInit {
const options: CustomizeDialogData = {
imageID: this.id,
selectedFormat: this.currentSelectedFormat,
formatOptions: this.simpleUtil.getBaseFormatOptions(),
formatOptions: this.utilService.getBaseFormatOptions(),
};
if (options.selectedFormat === 'original') {
options.selectedFormat = this.masterFileType.identifier;
}
await this.utilService.showCustomDialog(CustomizeDialogComponent, options, {
dismissable: false,
});
await this.dialogService.showCustomDialog(
CustomizeDialogComponent,
options,
{
dismissable: false,
},
);
}
@AutoUnsubscribe()
@ -224,7 +233,7 @@ export class ViewComponent implements OnInit {
});
}
newOptions = newOptions.concat(this.simpleUtil.getBaseFormatOptions());
newOptions = newOptions.concat(this.utilService.getBaseFormatOptions());
this.formatOptions = newOptions;
}

View File

@ -1,3 +1,4 @@
import { DialogModule } from '@angular/cdk/dialog';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
@ -13,6 +14,8 @@ import { CopyFieldModule } from 'src/app/components/copy-field/copy-field.module
import { FabModule } from 'src/app/components/fab/fab.module';
import { PicsurImgModule } from 'src/app/components/picsur-img/picsur-img.module';
import { PipesModule } from 'src/app/pipes/pipes.module';
import { DownloadManagerModule } from 'src/app/util/download-manager/dialog-manager.module';
import { ErrorManagerModule } from 'src/app/util/error-manager/error-manager.module';
import { CustomizeDialogComponent } from './customize-dialog/customize-dialog.component';
import { ViewComponent } from './view.component';
import { ViewRoutingModule } from './view.routing.module';
@ -20,6 +23,10 @@ import { ViewRoutingModule } from './view.routing.module';
declarations: [ViewComponent, CustomizeDialogComponent],
imports: [
CommonModule,
ErrorManagerModule,
DownloadManagerModule,
DialogModule,
CopyFieldModule,
ViewRoutingModule,
MatButtonModule,

View File

@ -21,7 +21,7 @@ import {
HasSuccess,
Open
} from 'picsur-shared/dist/types/failable';
import { SimpleUtilService } from 'src/app/util/util-module/simple-util.service';
import { UtilService } from 'src/app/util/util.service';
import { ImageUploadRequest } from '../../models/dto/image-upload-request.dto';
import { ApiService } from './api.service';
import { UserService } from './user.service';
@ -32,8 +32,7 @@ import { UserService } from './user.service';
export class ImageService {
constructor(
private readonly api: ApiService,
private readonly simpleUtil: SimpleUtilService,
private readonly util: UtilService,
private readonly userService: UserService,
) {}
@ -110,7 +109,7 @@ export class ImageService {
// Non api calls
public GetImageURL(image: string, filetype: string | null): string {
const baseURL = this.simpleUtil.getHost();
const baseURL = this.util.getHost();
const extension = FileType2Ext(filetype ?? '');
return `${baseURL}/i/${image}${

View File

@ -1,11 +1,8 @@
import { Inject, Injectable } from '@angular/core';
import { HISTORY } from '@ng-web-apis/common';
import { Injectable } from '@angular/core';
import { InfoResponse } from 'picsur-shared/dist/dto/api/info.dto';
import { AsyncFailable, Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { SemVerRegex } from 'picsur-shared/dist/util/common-regex';
import { BehaviorSubject } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { UtilService } from 'src/app/util/util-module/util.service';
import pkg from '../../../../package.json';
import { ServerInfo } from '../../models/dto/server-info.dto';
import { Logger } from '../logger/logger.service';
@ -23,13 +20,7 @@ export class InfoService {
private infoSubject = new BehaviorSubject<ServerInfo>(new ServerInfo());
constructor(
private readonly api: ApiService,
private readonly utilService: UtilService,
@Inject(HISTORY) private readonly history: History,
) {
this.checkCompatibility().catch(this.logger.error);
}
constructor(private readonly api: ApiService) {}
public async pollInfo(): AsyncFailable<ServerInfo> {
const response = await this.api.get(InfoResponse, '/api/info');
@ -72,46 +63,4 @@ export class InfoService {
return serverDecoded[0] === clientDecoded[0];
}
}
private async checkCompatibility() {
const isCompatible = await this.isCompatibleWithServer();
if (HasFailed(isCompatible)) {
this.utilService.showSnackBar(
'There was an error checking compatibility',
SnackBarType.Warning,
);
return;
}
if (!isCompatible) {
this.utilService
.showDialog({
title: 'Server is not compatible',
description:
'The server is not compatible with this version of the client. You can ignore this, but expect things to not work.',
buttons: [
{
text: 'Back',
name: 'back',
color: 'accent',
},
{
text: 'Ignore',
name: 'ignore',
color: 'warn',
},
],
})
.then((button) => {
if (button === 'ignore') {
this.logger.warn('Ignoring server compatibility');
} else {
this.checkCompatibility();
// Go to previous page
this.history.back();
}
});
}
}
}

View File

@ -11,11 +11,16 @@ import {
DecodedPref,
PrefValueType
} from 'picsur-shared/dist/dto/preferences.dto';
import { AsyncFailable, Fail, FT, HasFailed, Map } from 'picsur-shared/dist/types';
import {
AsyncFailable,
Fail,
FT,
HasFailed,
Map
} from 'picsur-shared/dist/types';
import { BehaviorSubject } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { Throttle } from 'src/app/util/throttle';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from '../logger/logger.service';
import { ApiService } from './api.service';
import { PermissionService } from './permission.service';
@ -41,7 +46,7 @@ export class SysPrefService {
constructor(
private readonly api: ApiService,
private readonly permissionsService: PermissionService,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {
this.subscribePermissions();
}
@ -49,10 +54,7 @@ export class SysPrefService {
private async refresh() {
const result = await this.getPreferences();
if (HasFailed(result)) {
this.utilService.showSnackBar(
"Couldn't load system preferences",
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
this.flush();
}
}

View File

@ -19,9 +19,8 @@ import {
Map
} from 'picsur-shared/dist/types';
import { BehaviorSubject } from 'rxjs';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { ErrorService } from 'src/app/util/error-manager/error.service';
import { Throttle } from 'src/app/util/throttle';
import { UtilService } from 'src/app/util/util-module/util.service';
import { Logger } from '../logger/logger.service';
import { ApiService } from './api.service';
import { PermissionService } from './permission.service';
@ -47,7 +46,7 @@ export class UsrPrefService {
constructor(
private readonly api: ApiService,
private readonly permissionsService: PermissionService,
private readonly utilService: UtilService,
private readonly errorService: ErrorService,
) {
this.subscribePermissions();
}
@ -55,10 +54,7 @@ export class UsrPrefService {
private async refresh() {
const result = await this.getPreferences();
if (HasFailed(result)) {
this.utilService.showSnackBar(
"Couldn't load user preferences",
SnackBarType.Error,
);
this.errorService.showFailure(result, this.logger);
this.flush();
}
}

View File

@ -0,0 +1,15 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SnackBarManagerModule } from '../snackbar-manager/snackbar-manager.module';
import { ApiErrorService } from './api-error.service';
@NgModule({
imports: [CommonModule, SnackBarManagerModule.forRoot()],
providers: [ApiErrorService],
})
export class ApiErrorManagerModule {
// Start apiErrorService, the nothing function does nothing, but it silents the error.
constructor(apiErrorService: ApiErrorService) {
apiErrorService.nothing();
}
}

View File

@ -3,7 +3,7 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { SnackBarType } from '../../models/dto/snack-bar-type.dto';
import { ApiService } from '../../services/api/api.service';
import { Logger } from '../../services/logger/logger.service';
import { UtilService } from './util.service';
import { SnackBarService } from '../snackbar-manager/snackbar.service';
@Injectable({
providedIn: 'root',
@ -13,7 +13,7 @@ export class ApiErrorService {
constructor(
private readonly apiSerivce: ApiService,
private readonly utilService: UtilService,
private readonly snackbarService: SnackBarService,
) {
this.subscribeErrors();
}
@ -28,7 +28,7 @@ export class ApiErrorService {
else url = error.url.url;
if (url.startsWith('/api')) {
this.utilService.showSnackBar('Network Error', SnackBarType.Error);
this.snackbarService.showSnackBar('Network Error', SnackBarType.Error);
}
this.logger.error(error.error);

View File

@ -11,6 +11,7 @@ export enum BSScreenSize {
lg = 3,
xl = 4,
xxl = 5,
xxxl = 6,
}
@Injectable({
@ -24,6 +25,7 @@ export class BootstrapService {
private lgObservable: Observable<boolean>;
private xlObservable: Observable<boolean>;
private xxlObservable: Observable<boolean>;
private xxxlObservable: Observable<boolean>;
private screenSizeSubject: BehaviorSubject<BSScreenSize> =
new BehaviorSubject<BSScreenSize>(BSScreenSize.xs);
@ -34,6 +36,7 @@ export class BootstrapService {
this.lgObservable = this.createObserver('(min-width: 992px)');
this.xlObservable = this.createObserver('(min-width: 1200px)');
this.xxlObservable = this.createObserver('(min-width: 1400px)');
this.xxxlObservable = this.createObserver('(min-width: 1600px)');
this.subscribeObservables();
}
@ -52,8 +55,11 @@ export class BootstrapService {
this.lgObservable,
this.xlObservable,
this.xxlObservable,
]).subscribe(([sm, md, lg, xl, xxl]) => {
const size = xxl
this.xxxlObservable,
]).subscribe(([sm, md, lg, xl, xxl, xxxl]) => {
const size = xxxl
? BSScreenSize.xxxl
: xxl
? BSScreenSize.xxl
: xl
? BSScreenSize.xl

View File

@ -0,0 +1,15 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { DialogManagerModule } from '../dialog-manager/dialog-manager.module';
import { ErrorManagerModule } from '../error-manager/error-manager.module';
import { CompatibilityService } from './compatibility.service';
@NgModule({
imports: [CommonModule, DialogManagerModule, ErrorManagerModule],
providers: [CompatibilityService],
})
export class CompatibilityManagerModule {
constructor(compatibilityService: CompatibilityService) {
compatibilityService.nothing();
}
}

View File

@ -0,0 +1,63 @@
import { Inject, Injectable } from '@angular/core';
import { HISTORY } from '@ng-web-apis/common';
import { HasFailed } from 'picsur-shared/dist/types';
import { InfoService } from 'src/app/services/api/info.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { DialogService } from '../dialog-manager/dialog.service';
import { ErrorService } from '../error-manager/error.service';
@Injectable({
providedIn: 'root',
})
export class CompatibilityService {
private readonly logger = new Logger(CompatibilityService.name);
constructor(
private readonly infoService: InfoService,
private readonly errorService: ErrorService,
private readonly dialogService: DialogService,
@Inject(HISTORY) private readonly history: History,
) {
this.checkCompatibility().catch(this.logger.error);
}
nothing() {}
private async checkCompatibility() {
const isCompatible = await this.infoService.isCompatibleWithServer();
if (HasFailed(isCompatible)) {
return this.errorService.showFailure(isCompatible, this.logger);
}
if (!isCompatible) {
this.dialogService
.showDialog({
title: 'Server is not compatible',
description:
'The server is not compatible with this version of the client. You can ignore this, but expect things to not work.',
buttons: [
{
text: 'Back',
name: 'back',
color: 'accent',
},
{
text: 'Ignore',
name: 'ignore',
color: 'warn',
},
],
})
.then((button) => {
if (button === 'ignore') {
this.logger.warn('Ignoring server compatibility');
} else {
this.checkCompatibility();
// Go to previous page
this.history.back();
}
});
}
}
}

View File

@ -0,0 +1,20 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
import { DialogService } from './dialog.service';
import { DownloadDialogComponent } from './download-dialog/download-dialog.component';
@NgModule({
imports: [
CommonModule,
MatDialogModule,
MatButtonModule,
MatProgressBarModule,
],
declarations: [ConfirmDialogComponent, DownloadDialogComponent],
providers: [DialogService],
})
export class DialogManagerModule {}

View File

@ -0,0 +1,45 @@
import { ComponentType } from '@angular/cdk/portal';
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Logger } from 'src/app/services/logger/logger.service';
import { ConfirmDialogComponent, ConfirmDialogData } from './confirm-dialog/confirm-dialog.component';
@Injectable({
providedIn: 'any',
})
export class DialogService {
private readonly logger = new Logger(DialogService.name);
constructor(
private readonly dialog: MatDialog,
) {}
public async showCustomDialog<T>(
component: ComponentType<T>,
data: any,
options?: {
dismissable?: boolean;
},
): Promise<any | undefined> {
return new Promise((resolve, reject) => {
const ref = this.dialog.open(component, {
data,
panelClass: 'small-dialog-padding',
...(options?.dismissable === false
? {}
: { disableClose: true, closeOnNavigation: false }),
maxHeight: '90vh',
});
const subscription = ref.beforeClosed().subscribe((result) => {
subscription.unsubscribe();
resolve(result);
});
});
}
public async showDialog(
options: ConfirmDialogData,
): Promise<string | undefined> {
return this.showCustomDialog(ConfirmDialogComponent, options);
}
}

View File

@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatDialogModule } from '@angular/material/dialog';
import { ErrorManagerModule } from '../error-manager/error-manager.module';
import { DownloadService } from './download.service';
@NgModule({
imports: [CommonModule, MatDialogModule, ErrorManagerModule],
providers: [DownloadService],
})
export class DownloadManagerModule {}

View File

@ -0,0 +1,117 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { ApiService } from 'src/app/services/api/api.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { DownloadDialogComponent } from '../dialog-manager/download-dialog/download-dialog.component';
import { ErrorService } from '../error-manager/error.service';
import { UtilService } from '../util.service';
@Injectable({
providedIn: 'any',
})
export class DownloadService {
private readonly logger = new Logger(DownloadService.name);
constructor(
private readonly dialog: MatDialog,
private readonly api: ApiService,
private readonly util: UtilService,
private readonly errorService: ErrorService,
) {}
public showDownloadDialog(filename: string): () => void {
const ref = this.dialog.open(DownloadDialogComponent, {
data: { name: filename },
disableClose: true,
closeOnNavigation: false,
});
return () => ref.close();
}
public async downloadFile(url: string) {
const closeDialog = this.showDownloadDialog('image');
const file = await this.api.getBuffer(url);
if (HasFailed(file))
return this.errorService.showFailure(file, this.logger);
this.util.downloadBuffer(file.buffer, file.name, file.mimeType);
closeDialog();
this.errorService.info('Image downloaded');
}
public canShare(): boolean {
if (navigator.canShare === undefined || navigator.share === undefined)
return false;
const testShare = navigator.canShare({
url: 'https://www.example.com',
});
return testShare;
}
public canShareFiles(): boolean {
if (!this.canShare()) return false;
const testFile = new File([], 'test.txt');
const testShare = navigator.canShare({
files: [testFile],
});
return testShare;
}
public async shareFile(url: string) {
if (!this.canShare())
return this.errorService.warn(
'Sharing is not supported on your device',
this.logger,
);
let shareObject: ShareData;
if (!this.canShareFiles()) {
shareObject = {
url,
};
} else {
const image = await this.api.getBuffer(url);
if (HasFailed(image))
return this.errorService.showFailure(image, this.logger);
this.logger.log(image.name, image.mimeType);
const imageFile = new File([image.buffer], image.name, {
type: image.mimeType,
});
shareObject = {
files: [imageFile],
};
}
const canShare = navigator.canShare(shareObject);
if (!canShare)
return this.errorService.warn(
'Sharing is not supported on your device',
this.logger,
);
try {
await navigator.share(shareObject);
} catch (e) {
if (e instanceof DOMException && e.message === 'Share canceled') {
} else {
this.errorService.showFailure(
Fail(FT.Internal, 'Sharing failed!', e),
this.logger,
);
}
}
}
}

View File

@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SnackBarManagerModule } from '../snackbar-manager/snackbar-manager.module';
import { ErrorService } from './error.service';
@NgModule({
imports: [CommonModule, SnackBarManagerModule.forRoot(), RouterModule],
providers: [ErrorService],
})
export class ErrorManagerModule {}

View File

@ -0,0 +1,66 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Failure } from 'picsur-shared/dist/types';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { Logger } from 'src/app/services/logger/logger.service';
import { SnackBarService } from '../snackbar-manager/snackbar.service';
@Injectable({
providedIn: 'any',
})
export class ErrorService {
constructor(
private readonly snackbar: SnackBarService,
private readonly router: Router,
) {}
public showFailure(error: Failure, logger: Logger): void {
if (error.isImportant()) {
logger.error(error.print());
} else {
logger.warn(error.print());
}
this.snackbar.showSnackBar(
error.getReason(),
error.isImportant() ? SnackBarType.Error : SnackBarType.Warning,
);
}
public quitFailure(error: Failure, logger: Logger): void {
this.showFailure(error, logger);
this.router.navigate(['/']);
}
public warn(warning: string, logger: Logger): void {
logger.warn(warning);
this.snackbar.showSnackBar(warning, SnackBarType.Warning);
}
public error(error: string, logger: Logger): void {
logger.error(error);
this.snackbar.showSnackBar(error, SnackBarType.Error);
}
public info(info: string) {
this.snackbar.showSnackBar(info, SnackBarType.Info);
}
public success(success: string) {
this.snackbar.showSnackBar(success, SnackBarType.Success);
}
public log(log: string) {
this.snackbar.showSnackBar(log, SnackBarType.Default);
}
public quitWarn(warning: string, logger: Logger): void {
this.warn(warning, logger);
this.router.navigate(['/']);
}
public quitError(error: string, logger: Logger): void {
this.error(error, logger);
this.router.navigate(['/']);
}
}

View File

@ -0,0 +1,28 @@
import { CommonModule } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core';
import {
MatSnackBarModule,
MAT_SNACK_BAR_DEFAULT_OPTIONS
} from '@angular/material/snack-bar';
import { SnackBarService } from './snackbar.service';
@NgModule({
imports: [CommonModule, MatSnackBarModule],
providers: [SnackBarService],
})
export class SnackBarManagerModule {
static forRoot(): ModuleWithProviders<SnackBarManagerModule> {
return {
ngModule: SnackBarManagerModule,
providers: [
{
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
useValue: {
duration: 4000,
horizontalPosition: 'left',
},
},
],
};
}
}

View File

@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
import { Logger } from 'src/app/services/logger/logger.service';
import { BootstrapService, BSScreenSize } from '../bootstrap.service';
@Injectable({
providedIn: 'any',
})
export class SnackBarService {
private readonly logger = new Logger(SnackBarService.name);
constructor(
private readonly snackBar: MatSnackBar,
private readonly bootstrap: BootstrapService,
) {}
public showSnackBar(
message: string,
type: SnackBarType = SnackBarType.Default,
duration: number | undefined | null = null,
) {
let ref = this.snackBar.open(message, '', {
panelClass: ['mat-toolbar', 'snackbar', type],
verticalPosition:
this.bootstrap.screenSizeSnapshot() > BSScreenSize.xs
? 'bottom'
: 'top',
...(duration !== null ? { duration } : {}),
});
}
}

View File

@ -1,46 +0,0 @@
import { CommonModule } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import {
MatSnackBarModule,
MAT_SNACK_BAR_DEFAULT_OPTIONS
} from '@angular/material/snack-bar';
import { RouterModule } from '@angular/router';
import { ApiErrorService } from './api-error.service';
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
import { DownloadDialogComponent } from './download-dialog/download-dialog.component';
@NgModule({
imports: [
CommonModule,
MatSnackBarModule,
MatDialogModule,
MatButtonModule,
MatProgressBarModule,
RouterModule,
],
declarations: [ConfirmDialogComponent, DownloadDialogComponent],
})
export class UtilModule {
static forRoot(): ModuleWithProviders<UtilModule> {
return {
ngModule: UtilModule,
providers: [
{
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
useValue: {
duration: 4000,
horizontalPosition: 'left',
},
},
],
};
}
// Start apiErrorService, the nothing function does nothing, but it silents the error.
constructor(apiErrorService: ApiErrorService) {
apiErrorService.nothing();
}
}

View File

@ -1,188 +0,0 @@
import { ComponentType } from '@angular/cdk/portal';
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { HasFailed } from 'picsur-shared/dist/types';
import { ApiService } from 'src/app/services/api/api.service';
import { Logger } from 'src/app/services/logger/logger.service';
import { SnackBarType } from '../../models/dto/snack-bar-type.dto';
import { BootstrapService, BSScreenSize } from './bootstrap.service';
import {
ConfirmDialogComponent,
ConfirmDialogData
} from './confirm-dialog/confirm-dialog.component';
import { DownloadDialogComponent } from './download-dialog/download-dialog.component';
import { SimpleUtilService } from './simple-util.service';
@Injectable({
providedIn: 'any',
})
export class UtilService {
private readonly logger = new Logger(UtilService.name);
constructor(
private readonly simpleUtil: SimpleUtilService,
private readonly snackBar: MatSnackBar,
private readonly dialog: MatDialog,
private readonly router: Router,
private readonly api: ApiService,
private readonly bootstrap: BootstrapService,
) {}
public quitError(message: string) {
this.showSnackBar(message, SnackBarType.Error);
this.router.navigate(['/']);
}
public showSnackBar(
message: string,
type: SnackBarType = SnackBarType.Default,
duration: number | undefined | null = null,
) {
let ref = this.snackBar.open(message, '', {
panelClass: ['mat-toolbar', 'snackbar', type],
verticalPosition:
this.bootstrap.screenSizeSnapshot() > BSScreenSize.xs
? 'bottom'
: 'top',
...(duration !== null ? { duration } : {}),
});
}
public async showCustomDialog<T>(
component: ComponentType<T>,
data: any,
options?: {
dismissable?: boolean;
},
): Promise<any | undefined> {
return new Promise((resolve, reject) => {
const ref = this.dialog.open(component, {
data,
panelClass: 'small-dialog-padding',
...(options?.dismissable === false
? {}
: { disableClose: true, closeOnNavigation: false }),
maxHeight: '90vh',
});
const subscription = ref.beforeClosed().subscribe((result) => {
subscription.unsubscribe();
resolve(result);
});
});
}
public async showDialog(
options: ConfirmDialogData,
): Promise<string | undefined> {
return this.showCustomDialog(ConfirmDialogComponent, options);
}
public showDownloadDialog(filename: string): () => void {
const ref = this.dialog.open(DownloadDialogComponent, {
data: { name: filename },
disableClose: true,
closeOnNavigation: false,
});
return () => ref.close();
}
public async downloadFile(url: string) {
const closeDialog = this.showDownloadDialog('image');
const file = await this.api.getBuffer(url);
if (HasFailed(file)) {
closeDialog();
this.logger.error(file.getReason());
this.showSnackBar('Error while downloading image', SnackBarType.Error);
return;
}
this.simpleUtil.downloadBuffer(file.buffer, file.name, file.mimeType);
closeDialog();
this.showSnackBar('Image downloaded', SnackBarType.Info);
}
public canShare(): boolean {
if (navigator.canShare === undefined || navigator.share === undefined)
return false;
const testShare = navigator.canShare({
url: 'https://www.example.com',
});
return testShare;
}
public canShareFiles(): boolean {
if (!this.canShare()) return false;
const testFile = new File([], 'test.txt');
const testShare = navigator.canShare({
files: [testFile],
});
return testShare;
}
public async shareFile(url: string) {
if (!this.canShare()) {
this.showSnackBar(
'Sharing is not supported on your device',
SnackBarType.Warning,
);
return;
}
let shareObject: ShareData;
if (!this.canShareFiles()) {
shareObject = {
url,
};
} else {
const image = await this.api.getBuffer(url);
if (HasFailed(image)) {
this.logger.error(image.getReason());
this.showSnackBar('Error while sharing image', SnackBarType.Error);
return;
}
this.logger.log(image.name, image.mimeType);
const imageFile = new File([image.buffer], image.name, {
type: image.mimeType,
});
shareObject = {
files: [imageFile],
};
}
const canShare = navigator.canShare(shareObject);
if (!canShare) {
this.showSnackBar(
'Sharing is not supported on your device',
SnackBarType.Warning,
);
return;
}
try {
await navigator.share(shareObject);
} catch (e) {
if (e instanceof DOMException && e.message === 'Share canceled') {
} else {
this.logger.error(e);
this.showSnackBar('Could not share', SnackBarType.Error);
}
}
}
public async sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}

View File

@ -2,13 +2,13 @@ import { Inject, Injectable } from '@angular/core';
import { LOCATION } from '@ng-web-apis/common';
import { FileType2Ext, SupportedFileTypes } from 'picsur-shared/dist/dto/mimes.dto';
import { HasFailed } from 'picsur-shared/dist/types';
import { Logger } from '../../services/logger/logger.service';
import { Logger } from '../services/logger/logger.service';
@Injectable({
providedIn: 'any',
})
export class SimpleUtilService {
private readonly logger = new Logger(SimpleUtilService.name);
export class UtilService {
private readonly logger = new Logger(UtilService.name);
constructor(@Inject(LOCATION) private readonly location: Location) {}
@ -49,4 +49,8 @@ export class SimpleUtilService {
return newOptions;
}
public async sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}