Add file upload functionality with dialog components and update image handling

This commit is contained in:
liosha84
2025-08-05 21:13:21 +03:00
parent 8a18307527
commit d2f46f4405
33 changed files with 471 additions and 200 deletions
@@ -1,33 +1,23 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, Inject, inject, Renderer2} from '@angular/core';
import {AsyncPipe, DOCUMENT, NgOptimizedImage} from "@angular/common";
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
import {AsyncPipe} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {MatButton, MatMiniFabButton} from "@angular/material/button";
import {
MatCard,
MatCardActions,
MatCardContent,
MatCardHeader,
MatCardImage,
MatCardXlImage
MatCardHeader
} from "@angular/material/card";
import {MatFormField, MatInput, MatLabel} from "@angular/material/input";
import {Image} from '../../../models/image';
import {ZhipuaiImageService} from '../zhipuai-image.service';
import {SpinnerService} from '../../../services/spinner.service';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
import {FileUploadService} from '../../../services/file-upload-api.service';
import {Observable, Subscription} from 'rxjs';
import {HttpClient, HttpResponse} from '@angular/common/http';
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
import {AuthService} from '../../../services/auth.service';
import {TokenStorageService} from '../../../services/token-storage.service';
import {FileInfo} from '../../../models/file-info';
import {UserApiService} from '../../user-module/user-api.service';
import {MatProgressBar} from '@angular/material/progress-bar';
@Component({
selector: 'app-generate-image.component',
@@ -43,26 +33,17 @@ import {MatProgressBar} from '@angular/material/progress-bar';
MatInput,
MatLabel,
MatProgressSpinner,
NgOptimizedImage,
MatCardXlImage,
MatCardImage,
MatToolbarRow,
MatToolbar,
MatIcon,
MatMiniFabButton,
MatTooltip,
MatProgressBar
],
templateUrl: './generate-image.component.html',
styleUrl: './generate-image.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GenerateImageComponent {
isSpinnerVisible = false;
query : string = '';
images: Image[] = [];
@@ -70,10 +51,6 @@ export class GenerateImageComponent {
url: ''
};
currentFile?: File;
message = '';
isLoggedIn = false;
private storageService: TokenStorageService = inject(TokenStorageService);
@@ -81,13 +58,8 @@ export class GenerateImageComponent {
constructor(
private zhipuaiImageService: ZhipuaiImageService,
public spinnerService: SpinnerService,
private uploadService: FileUploadService,
private userApiService:UserApiService,
private userApiService:UserApiService
) {
this.isLoggedIn = this.storageService.isLoggedIn();
}
@@ -108,7 +80,6 @@ export class GenerateImageComponent {
)
}
save(url: string | undefined) {
let requestUrl:string = url?url:"";
console.log(requestUrl);
@@ -118,9 +89,6 @@ export class GenerateImageComponent {
console.log(error);
});
}
}
@@ -2,13 +2,15 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {tutorialsRouting} from './tutorials.routing';
import {TutorialsComponent} from './tutorials.component/tutorials.component';
import {MatFormFieldModule} from '@angular/material/form-field';
@NgModule({
declarations: [],
imports: [
tutorialsRouting,
TutorialsComponent,
CommonModule
CommonModule,
MatFormFieldModule
]
})
export class TutorialsModule { }
@@ -1,5 +1,5 @@
<h1 mat-dialog-title>Hi </h1>
<div mat-dialog-content >
<!--<div mat-dialog-content >-->
<mat-dialog-content class="mat-typography">
<!-- <mat-nav-list>-->
@for (fileInfo of fileInfos; track fileInfo){
@@ -16,7 +16,7 @@
<!-- </mat-nav-list>-->
</mat-dialog-content>
</div>
<!--</div>-->
<div mat-dialog-actions>
<button mat-button (click)="upload()">Upload</button>
<span class="menu-spacer"></span>
@@ -2,20 +2,30 @@
flex: 1 1 auto;
}
.mat-dialog-content{
.mat-mdc-list-item{
}
a.mdc-list-item
{
cursor: grab;
height: 120px;
padding-bottom: 66px;
background-color: rgba(24,255,255,0.04);
}
/*.mat-dialog-content{
min-height: 300px;
min-width: 300px;
height: 75%;
width: 75%;
}
.mdc-dialog--open .mat-mdc-dialog-inner-container
}*/
/*.mdc-dialog--open .mat-mdc-dialog-inner-container
{
opacity: 1;
width: 600px;
}
}*/
.mat-mdc-dialog-container {
/*.mat-mdc-dialog-container {
width: 600px;
height: 500px;
display: block;
@@ -25,4 +35,9 @@
min-width: inherit;
max-width: inherit;
outline: 0;
}*/
/*
.mat-mdc-dialog-content{
overflow: auto;
}
*/
@@ -7,8 +7,7 @@ import {
MatDialogTitle
} from '@angular/material/dialog';
import {MatButton} from '@angular/material/button';
import {MatListItem, MatNavList} from '@angular/material/list';
import {RouterLink} from '@angular/router';
import {MatListItem} from '@angular/material/list';
import {FileInfo} from '../../../models/file-info';
import {UserApiService} from '../user-api.service';
@@ -20,10 +19,7 @@ import {UserApiService} from '../user-api.service';
MatButton,
MatDialogActions,
MatDialogClose,
MatListItem,
MatNavList,
MatListItem
],
templateUrl: './dialog-select-image.component.html',
styleUrl: './dialog-select-image.component.scss'
@@ -42,10 +38,10 @@ export class DialogSelectImageComponent {
console.log(data);
});
}
//open upload dialog
upload() {
this.uploadClicked.emit();
}
onNoClick() {
@@ -54,5 +50,6 @@ export class DialogSelectImageComponent {
select(fileInfo:FileInfo) {
this.selectClicked.emit(fileInfo);
this.dialogRef.close();
}
}
@@ -0,0 +1,14 @@
<p>dialog-upload-image.component works!</p>
<mat-dialog-content class="mat-typography">
<app-file-upload #imageUpload (onImageUploaded)="imageUpload_ImageUploaded($event)"></app-file-upload>
</mat-dialog-content>
<div mat-dialog-actions>
<button mat-button (click)="openSelectImageDialog()">Select image</button>
<span class="menu-spacer"></span>
<button mat-button mat-dialog-close (click)="onNoClick()">No Thanks</button>
<!-- <button mat-button cdkFocusInitial (click)="select()">Ok</button>-->
<button mat-button cdkFocusInitial [disabled]="!imageUploaded" (click)="selectUploadedImage()">Ok</button>
</div>
@@ -0,0 +1,6 @@
.menu-spacer {
flex: 1 1 auto;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DialogUploadImageComponent } from './dialog-upload-image.component';
describe('DialogUploadImageComponent', () => {
let component: DialogUploadImageComponent;
let fixture: ComponentFixture<DialogUploadImageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DialogUploadImageComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DialogUploadImageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,54 @@
import {Component, EventEmitter, inject, Output, ViewChild} from '@angular/core';
import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
import {MatDialogActions, MatDialogClose, MatDialogContent, MatDialogRef} from '@angular/material/dialog';
import {MatButton} from '@angular/material/button';
import {FileInfo} from '../../../models/file-info';
@Component({
selector: 'app-dialog-upload-image.component',
imports: [
FileUploadComponent,
MatDialogContent,
MatButton,
MatDialogActions,
MatDialogClose
],
templateUrl: './dialog-upload-image.component.html',
styleUrl: './dialog-upload-image.component.scss'
})
export class DialogUploadImageComponent {
//select uploaded file
@Output() selectUploadedImageClicked = new EventEmitter<any>();
//open select dialog
@Output() openSelectDialogClicked = new EventEmitter<any>();
readonly dialogRef = inject(MatDialogRef<DialogUploadImageComponent>);
@ViewChild('imageUpload') imageUpload: FileUploadComponent | undefined;
imageUploaded:boolean = false;
onNoClick() {
this.dialogRef.close();
}
selectUploadedImage() {
this.selectUploadedImageClicked.emit(this.imageUpload?.fileInfo?.url);
this.dialogRef.close();
}
openSelectImageDialog() {
this.openSelectDialogClicked.emit();
this.dialogRef.close();
}
imageUpload_ImageUploaded($event: FileInfo | undefined) {
if($event){
this.imageUploaded = true;
}
else{
this.imageUploaded = false;
}
}
}
@@ -14,9 +14,9 @@
<img style="width: 120px; height: 120px" src="{{tutorial.titleimage}}" alt="">
<input matInput
[(ngModel)]="tutorial.titleimage">
<button mat-raised-button color="primary" (click)="openSelectImageDialog('100ms', '5ms')">Select image</button>
</mat-form-field>
<button mat-raised-button color="primary" (click)="openSelectImageDialog('100ms', '5ms')">Select image</button>
<button mat-raised-button color="primary" (click)="openUploadImageDialog('100ms', '5ms')">Upload image</button>
<div class="example-full-width">
<mat-label>Description</mat-label>
<mat-tab-group>
@@ -12,10 +12,10 @@ import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
import {DialogLoginComponent} from '../../main-module/main.component/dialog-login.component/dialog-login.component';
import {GlobalConstants} from '../../../global-constants';
import {MatDialog} from '@angular/material/dialog';
import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
@Component({
selector: 'app-tutorial-add.component',
@@ -51,6 +51,8 @@ export class TutorialAddComponent implements OnInit{
readonly dialog = inject(MatDialog);
markdown = `## Markdown __rulez__!
---
@@ -72,10 +74,6 @@ const language = 'typescript';
constructor(private fb: FormBuilder,
private markdownService: MarkdownService,
private userApiService: UserApiService,
private storageService: TokenStorageService,
private eventBusService: EventBusService,
private router: Router,
private authService: AuthService
) {
this.tutorial.description = this.markdown;
@@ -185,7 +183,7 @@ const language = 'typescript';
});
dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
dialogSelectRef.close();
//this.openSignupDialog(enterAnimationDuration, exitAnimationDuration);
this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
})
const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
@@ -199,4 +197,29 @@ const language = 'typescript';
});
}
openUploadImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
let dialogSelectRef = this.dialog.open(DialogUploadImageComponent, {
height: '500px',
width: '600px',
enterAnimationDuration,
exitAnimationDuration,
// data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
});
dialogSelectRef.componentInstance.openSelectDialogClicked.subscribe(result => {
dialogSelectRef.close();
this.openSelectImageDialog(enterAnimationDuration, exitAnimationDuration);
})
const dialogUploadSubscription = dialogSelectRef.componentInstance.selectUploadedImageClicked
.subscribe(result => {
console.log('Got the data!', result);
if (result == null) {
return;
}
this.tutorial.titleimage = result.url;
});
}
}
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import {forkJoin, Observable} from 'rxjs';
import {Tutorial} from '../../models/tutorial.model';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {HttpClient, HttpEvent, HttpHeaders, HttpRequest} from '@angular/common/http';
import {GlobalConstants} from '../../global-constants';
import {FileInfo} from '../../models/file-info';
@@ -15,6 +15,19 @@ export class UserApiService {
}
upload(file: File): Observable<HttpEvent<any>> {
const formData: FormData = new FormData();
formData.append('file', file);
const req = new HttpRequest('POST', `${this.baseUrl}/upload`, formData, {
reportProgress: true,
responseType: 'json'
});
return this.http.request(req);
}
saveZhipuaiImage(url: string): Observable<any> {
return this.http.post(`${this.baseUrl}/saveZhipuAiImage`, {imageUrl:url});
}
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import {userRouting} from './user.routing';
import {UserComponent} from './user.component/user.component';
import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
import {MatFormFieldModule} from '@angular/material/form-field';
@@ -12,7 +13,8 @@ import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
userRouting,
UserComponent,
CommonModule,
AngularMarkdownEditorModule
AngularMarkdownEditorModule,
MatFormFieldModule
]
})
export class UserModule { }