Fixing Angular 21 by migrating all values by signals. (#11)
Some checks failed
Build and Deploy Java Gradle Application / build-and-deploy (push) Failing after 53s
Some checks failed
Build and Deploy Java Gradle Application / build-and-deploy (push) Failing after 53s
This commit was merged in pull request #11.
This commit is contained in:
@@ -1,50 +1,50 @@
|
||||
@if (isLoading) {
|
||||
<h2 i18n>Publication content loading...</h2>
|
||||
<mat-spinner></mat-spinner>
|
||||
@if (isLoading()) {
|
||||
<h2 i18n>Publication content loading...</h2>
|
||||
<mat-spinner></mat-spinner>
|
||||
} @else {
|
||||
@if (publication) {
|
||||
<div class="card">
|
||||
<img src="/api/pictures/{{ publication.illustrationId }}" />
|
||||
<header>
|
||||
<h1>{{ publication.title }}</h1>
|
||||
<h2>{{ publication.description }}</h2>
|
||||
@if (isAuthorAndUserEquals) {
|
||||
<a [routerLink]="['edit']"
|
||||
class="button action"
|
||||
matTooltip="Click to edit the publication"
|
||||
matRipple
|
||||
i18n-matTooltip>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</a>
|
||||
}
|
||||
</header>
|
||||
<main [innerHTML]="publication.parsedText"></main>
|
||||
<footer>
|
||||
<div class="metadata">
|
||||
<img src="/api/pictures/{{ publication.author.image }}" [matTooltip]="publication.author.name" />
|
||||
<div class="posting-data">
|
||||
<span i18n>Publication posted by {{ publication.author.name }}</span>
|
||||
<span class="publication-date">
|
||||
@if (publication(); as publication) {
|
||||
<div class="card">
|
||||
<img src="/api/pictures/{{ publication.illustrationId }}"/>
|
||||
<header>
|
||||
<h1>{{ publication.title }}</h1>
|
||||
<h2>{{ publication.description }}</h2>
|
||||
@if (isAuthorAndUserEquals()) {
|
||||
<a [routerLink]="['edit']"
|
||||
class="button action"
|
||||
matTooltip="Click to edit the publication"
|
||||
matRipple
|
||||
i18n-matTooltip>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</a>
|
||||
}
|
||||
</header>
|
||||
<main [innerHTML]="publication.parsedText"></main>
|
||||
<footer>
|
||||
<div class="metadata">
|
||||
<img src="/api/pictures/{{ publication.author.image }}" [matTooltip]="publication.author.name"/>
|
||||
<div class="posting-data">
|
||||
<span i18n>Publication posted by {{ publication.author.name }}</span>
|
||||
<span class="publication-date">
|
||||
({{ publication.creationDate | date: 'short' }})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@if (isAuthorAndUserEquals) {
|
||||
<button type="button"
|
||||
(click)="deletePublication()"
|
||||
matTooltip="Click to delete the publication"
|
||||
matTooltipPosition="left"
|
||||
matRipple
|
||||
i18n-matTooltip>
|
||||
<mat-icon>delete</mat-icon>
|
||||
Delete
|
||||
</button>
|
||||
}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="loading-failed">
|
||||
<h1 i18n>Publication failed to load...</h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (isAuthorAndUserEquals()) {
|
||||
<button type="button"
|
||||
(click)="deletePublication()"
|
||||
matTooltip="Click to delete the publication"
|
||||
matTooltipPosition="left"
|
||||
matRipple
|
||||
i18n-matTooltip>
|
||||
<mat-icon>delete</mat-icon>
|
||||
Delete
|
||||
</button>
|
||||
}
|
||||
</footer>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="loading-failed">
|
||||
<h1 i18n>Publication failed to load...</h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
$cardBorderRadius: .5em;
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.card {
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1em;
|
||||
max-width: 80em;
|
||||
width: 90%;
|
||||
border-radius: .5em;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .16), 0 2px 10px 0 rgba(0, 0, 0, .12);
|
||||
|
||||
img {
|
||||
height: 12em;
|
||||
object-fit: cover;
|
||||
border-radius: $cardBorderRadius $cardBorderRadius 0 0;
|
||||
transition: height .2s ease-in-out;
|
||||
|
||||
@media screen and (min-width: 450px) {
|
||||
height: 15em;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
height: 20em;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
height: 25em;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 2em;
|
||||
position: relative;
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.6em;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: .8em .8em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
position: absolute;
|
||||
top: -1.5em;
|
||||
right: 3em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1em;
|
||||
max-width: 80em;
|
||||
width: 90%;
|
||||
border-radius: .5em;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #5b6ed8;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
border-top: 1px solid #dddddd;
|
||||
margin: 0 2em 2em 2em;
|
||||
padding-top: 2em;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 0 0 $cardBorderRadius $cardBorderRadius;
|
||||
padding: 1em 2em;
|
||||
gap: 1em;
|
||||
|
||||
@media screen and (min-width: 500px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
|
||||
img {
|
||||
height: 12em;
|
||||
object-fit: cover;
|
||||
border-radius: $cardBorderRadius $cardBorderRadius 0 0;
|
||||
transition: height .2s ease-in-out;
|
||||
|
||||
@media screen and (min-width: 450px) {
|
||||
height: 15em;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
height: 20em;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
height: 25em;
|
||||
}
|
||||
$imageSize: 4em;
|
||||
border-radius: 10em;
|
||||
width: $imageSize;
|
||||
height: $imageSize;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 2em;
|
||||
position: relative;
|
||||
.posting-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.6em;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: .8em .8em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #3f51b5;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
position: absolute;
|
||||
top: -1.5em;
|
||||
right: 3em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #5b6ed8;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.publication-date {
|
||||
font-style: italic;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
border-top: 1px solid #dddddd;
|
||||
margin: 0 2em 2em 2em;
|
||||
padding-top: 2em;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 0 0 $cardBorderRadius $cardBorderRadius;
|
||||
padding: 1em 2em;
|
||||
gap: 1em;
|
||||
|
||||
@media screen and (min-width: 500px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.metadata {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
|
||||
img {
|
||||
$imageSize: 4em;
|
||||
border-radius: 10em;
|
||||
width: $imageSize;
|
||||
height: $imageSize;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.posting-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
|
||||
.publication-date {
|
||||
font-style: italic;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: .8em 1.2em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #D50000;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: .5em;
|
||||
|
||||
&:hover {
|
||||
background-color: #E53935;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
button {
|
||||
padding: .8em 1.2em;
|
||||
border-radius: 10em;
|
||||
border: none;
|
||||
background-color: #D50000;
|
||||
color: white;
|
||||
transition: background-color .2s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: .5em;
|
||||
|
||||
&:hover {
|
||||
background-color: #E53935;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { CommonModule, Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ConfirmationDialog } from '../../components/confirmation-dialog/confirmation-dialog.component';
|
||||
import { Publication } from '../../core/rest-services/publications/model/publication';
|
||||
import { PublicationRestService } from '../../core/rest-services/publications/publication.rest-service';
|
||||
import { AuthenticationService } from '../../core/service/authentication.service';
|
||||
import {CommonModule, Location} from '@angular/common';
|
||||
import {Component, inject, OnDestroy, OnInit, signal} from '@angular/core';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatProgressSpinner} from '@angular/material/progress-spinner';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {ActivatedRoute, RouterModule} from '@angular/router';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {ConfirmationDialog} from '../../components/confirmation-dialog/confirmation-dialog.component';
|
||||
import {Publication} from '../../core/rest-services/publications/model/publication';
|
||||
import {PublicationRestService} from '../../core/rest-services/publications/publication.rest-service';
|
||||
import {AuthenticationService} from '../../core/service/authentication.service';
|
||||
|
||||
declare let Prism: any;
|
||||
|
||||
@Component({
|
||||
selector: 'app-publication',
|
||||
templateUrl: './publication.component.html',
|
||||
styleUrl: './publication.component.scss',
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIcon,
|
||||
MatRippleModule,
|
||||
MatProgressSpinner,
|
||||
MatTooltipModule,
|
||||
RouterModule
|
||||
]
|
||||
selector: 'app-publication',
|
||||
templateUrl: './publication.component.html',
|
||||
styleUrl: './publication.component.scss',
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIcon,
|
||||
MatRippleModule,
|
||||
MatProgressSpinner,
|
||||
MatTooltipModule,
|
||||
RouterModule
|
||||
]
|
||||
})
|
||||
export class PublicationComponent implements OnInit, OnDestroy {
|
||||
private readonly activatedRoute = inject(ActivatedRoute);
|
||||
@@ -37,35 +37,35 @@ export class PublicationComponent implements OnInit, OnDestroy {
|
||||
private readonly snackBar = inject(MatSnackBar);
|
||||
private paramMapSubscription?: Subscription;
|
||||
private afterDialogCloseSubscription?: Subscription;
|
||||
isLoading: boolean = false;
|
||||
isAuthorAndUserEquals: boolean = false;
|
||||
publication?: Publication;
|
||||
isLoading = signal(false);
|
||||
isAuthorAndUserEquals = signal(false);
|
||||
publication = signal<Publication | null>(null);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.paramMapSubscription = this.activatedRoute
|
||||
.paramMap
|
||||
.subscribe(params => {
|
||||
const publicationId = params.get('publicationId');
|
||||
.paramMap
|
||||
.subscribe(params => {
|
||||
const publicationId = params.get('publicationId');
|
||||
|
||||
if (publicationId) {
|
||||
this.isLoading = true;
|
||||
if (publicationId) {
|
||||
this.isLoading.set(true);
|
||||
|
||||
this.publicationRestService.getById(publicationId)
|
||||
.then(publication => {
|
||||
this.publication = publication;
|
||||
this.isAuthorAndUserEquals = this.authenticationService.getAuthenticatedUser()?.id === this.publication.author.id;
|
||||
setTimeout(() => Prism.highlightAll(), 100);
|
||||
})
|
||||
.catch(error => {
|
||||
const errorMessage = $localize`An error occurred while loading publication...`;
|
||||
this.snackBar.open(errorMessage, $localize`Close`, { duration: 5000 });
|
||||
console.error(errorMessage, error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
this.publicationRestService.getById(publicationId)
|
||||
.then(publication => {
|
||||
this.publication.set(publication);
|
||||
this.isAuthorAndUserEquals.set(this.authenticationService.getAuthenticatedUser()?.id === this.publication()?.author.id);
|
||||
setTimeout(() => Prism.highlightAll(), 100);
|
||||
})
|
||||
.catch(error => {
|
||||
const errorMessage = $localize`An error occurred while loading publication...`;
|
||||
this.snackBar.open(errorMessage, $localize`Close`, {duration: 5000});
|
||||
console.error(errorMessage, error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading.set(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -86,9 +86,10 @@ export class PublicationComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.afterDialogCloseSubscription = dialogRef.afterClosed()
|
||||
.subscribe(response => {
|
||||
if (response && this.publication?.id) {
|
||||
this.publicationRestService.delete(this.publication.id);
|
||||
this.snackBar.open($localize`Publication deleted`, $localize`Close`, { duration: 5000 });
|
||||
const publication = this.publication();
|
||||
if (response && publication?.id) {
|
||||
this.publicationRestService.delete(publication.id);
|
||||
this.snackBar.open($localize`Publication deleted`, $localize`Close`, {duration: 5000});
|
||||
this.location.back();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user