Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into 66-frontend-enhanc…
Browse files Browse the repository at this point in the history
…ements
  • Loading branch information
larsuzh committed May 8, 2024
2 parents 9829d0a + 35211d0 commit f58014b
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 106 deletions.
4 changes: 4 additions & 0 deletions src/app/services/api/location-riddle-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export class LocationRiddleApiService {
.pipe(map((dto) => this.mapDtoToLocationRiddle(dto)));
}

deleteLocationRiddle(locationRiddleId: string): Observable<void> {
return this.http.delete<void>(environment.api.url + '/location-riddles/' + locationRiddleId);
}

private getRequest(url: string): Observable<LocationRiddle[]> {
return this.http.get<LocationRiddleDto[]>(environment.api.url + url).pipe(
map((dtos: LocationRiddleDto[]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class LocationRiddleStateService {
public submittedGuess = new Subject<void>();
public commentOnLocationRiddle = new Subject<string>();
public rateLocationRiddle = new Subject<number>();
public deleteLocationRiddle = new Subject<string>();

// Sources
private commentOnLocationRiddleSource$ = this.commentOnLocationRiddle.pipe(
Expand All @@ -73,6 +74,9 @@ export class LocationRiddleStateService {
this.locationRiddleApiService.rateLocationRiddle(this.locationRiddle()?.locationRiddleId || '', rating)
)
);
private deleteLocationRiddleSource$ = this.deleteLocationRiddle.pipe(
switchMap((locationRiddleId) => this.locationRiddleApiService.deleteLocationRiddle(locationRiddleId))
);

constructor() {
const locationRiddleChangeSources = merge(
Expand All @@ -88,6 +92,7 @@ export class LocationRiddleStateService {
.with(this.mapZoomChanged, (state, mapZoom) => ({ mapZoom }))
.with(this.mapCenterChanged, (state, mapCenter) => ({ mapCenter }))
.with(this.setUsername, (state, username) => ({ username }))
.with(this.deleteLocationRiddleSource$, (state) => ({ locationRiddle: undefined }))
.with(locationRiddleChangeSources, (state, locationRiddle) => ({ locationRiddle }));
}
}
211 changes: 111 additions & 100 deletions src/app/shared/location-riddle-post/location-riddle-post.component.html
Original file line number Diff line number Diff line change
@@ -1,106 +1,117 @@
@if (locationRiddleState.locationRiddle()) {
<ion-card style="width: 90vw; min-height: 37rem; max-width: 40rem" color="light">
<ion-card-header class="ion-padding">
<ion-card-title>
<ion-item lines="none">
<ion-avatar aria-hidden="true" slot="start">
<ion-img src="https://ionicframework.com/docs/img/demos/avatar.svg"></ion-img>
</ion-avatar>
<div style="display: flex; justify-content: space-between; flex-grow: 1">
<ion-label class="hover-pointer" color="dark"
(click)="router.navigate(['/home/profiles/' + locationRiddleState.locationRiddle()!.username])">
<h2>{{ locationRiddleState.locationRiddle()!.username }}</h2>
<p>{{ locationRiddleState.locationRiddle()!.createdAt | date: 'short' }}</p>
</ion-label>
<app-rating
[rating]="locationRiddleState.locationRiddle()!.rating"
(makeRating)="locationRiddleState.rateLocationRiddle.next($event)"
[ratingError]="ratingError"
></app-rating>
</div>
</ion-item>
</ion-card-title>
</ion-card-header>
<ion-card-content style="height: 40vh; min-height: 30rem">
@if (locationRiddleState.showMap()) {
<app-map
(placeMarker)="locationRiddleState.placeGuess.next($event)"
[marker]="locationRiddleState.marker()"
[guesses]="locationRiddleState.guesses()"
[userGuess]="locationRiddleState.userGuess()"
[solution]="locationRiddleState.solution()"
[solved]="locationRiddleState.locationRiddle()!.solved"
[zoom]="locationRiddleState.mapZoom()"
[center]="locationRiddleState.mapCenter()"
(zoomChange)="locationRiddleState.mapZoomChanged.next($event)"
(centerChange)="locationRiddleState.mapCenterChanged.next($event)"
></app-map>
} @else {
<ion-img
src="data:image;base64, {{ locationRiddleState.locationRiddle()!.locationRiddleImage }}"
></ion-img>
}
<ion-item
expand="block"
class="ion-no-padding"
lines="none"
style="margin-top: 15px; margin-bottom: 5px"
<ion-card style="width: 90vw; min-height: 37rem; max-width: 40rem" color="light">
<ion-card-header class="ion-padding">
<ion-card-title>
<ion-item lines="none">
<div style="display: flex; justify-content: space-between; flex-grow: 1">
<ion-label class="hover-pointer" color="dark" (click)="router.navigate(['/home/profiles/' + locationRiddleState.locationRiddle()!.username])">
<h2>{{ locationRiddleState.locationRiddle()!.username }}</h2>
<p>{{ locationRiddleState.locationRiddle()!.createdAt | date: 'short' }}</p>
</ion-label>
<app-rating
[rating]="locationRiddleState.locationRiddle()!.rating"
(makeRating)="locationRiddleState.rateLocationRiddle.next($event)"
[ratingError]="ratingError"
></app-rating>
</div>
@if (isCurrentUser()) {
<ion-button [id]="'present-alert-' + locationRiddle().locationRiddleId" fill="clear" slot="end">
<ion-icon
class="hover-pointer"
name="trash-outline"
color="dark"
size="medium"
></ion-icon>
</ion-button>
<ion-alert
[trigger]="'present-alert-' + locationRiddle().locationRiddleId"
header="Are you sure you want to delete this riddle?"
[buttons]="alertButtons"
></ion-alert>
}
</ion-item>
</ion-card-title>
</ion-card-header>
<ion-card-content style="height: 40vh; min-height: 30rem">
@if (locationRiddleState.showMap()) {
<app-map
(placeMarker)="locationRiddleState.placeGuess.next($event)"
[marker]="locationRiddleState.marker()"
[guesses]="locationRiddleState.guesses()"
[userGuess]="locationRiddleState.userGuess()"
[solution]="locationRiddleState.solution()"
[solved]="locationRiddleState.locationRiddle()!.solved"
[zoom]="locationRiddleState.mapZoom()"
[center]="locationRiddleState.mapCenter()"
(zoomChange)="locationRiddleState.mapZoomChanged.next($event)"
(centerChange)="locationRiddleState.mapCenterChanged.next($event)"
></app-map>
} @else {
<ion-img
src="data:image;base64, {{ locationRiddleState.locationRiddle()!.locationRiddleImage }}"
></ion-img>
}
<ion-item
expand="block"
class="ion-no-padding"
lines="none"
style="margin-top: 15px; margin-bottom: 5px"
(click)="commentsModalOpen = true"
>
<ion-icon size="small" name="chatbox-outline"></ion-icon>
<p style="padding-left: 0.3rem">add a comment...</p>
</ion-item>
<ion-fab horizontal="start" slot="fixed" vertical="bottom">
<ion-fab-button (click)="locationRiddleState.toggleMap.next()">
<ion-icon name="{{ locationRiddleState.showMap() ? 'image' : 'map' }}-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
@if (locationRiddleState.marker() && !locationRiddleState.submitted()) {
<ion-fab horizontal="end" slot="fixed" vertical="bottom">
<ion-fab-button color="success" (click)="submit()">
<ion-icon color="dark" name="checkmark-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
}
</ion-card-content>
</ion-card>
>
<ion-icon size="small" name="chatbox-outline"></ion-icon>
<p style="padding-left: 0.3rem">add a comment...</p>
</ion-item>
<ion-fab horizontal="start" slot="fixed" vertical="bottom">
<ion-fab-button (click)="locationRiddleState.toggleMap.next()">
<ion-icon name="{{ locationRiddleState.showMap() ? 'image' : 'map' }}-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
@if (locationRiddleState.marker() && !locationRiddleState.submitted()) {
<ion-fab horizontal="end" slot="fixed" vertical="bottom">
<ion-fab-button color="success" (click)="submit()">
<ion-icon color="dark" name="checkmark-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
}
</ion-card-content>
</ion-card>

<ion-modal
#modal
[breakpoints]="[0, 0.5]"
[initialBreakpoint]="0.5"
<ion-modal
#modal
[breakpoints]="[0, 0.5]"
[initialBreakpoint]="0.5"
[isOpen]="commentsModalOpen"
(didDismiss)="commentsModalOpen = false"
class="comments"
>
<ng-template>
<ion-item lines="full">
<ion-label class="ion-text-center">
<h2>Comments</h2>
</ion-label>
</ion-item>
<ion-content>
@for (comment of locationRiddleState.locationRiddle()!.comments; track comment.username) {
<ion-item lines="none">
<ion-label class="ion-text-wrap" (click)="router.navigate(['/home/profiles/' + comment.username])">
<h3>{{ comment.username }}</h3>
<p>{{ comment.comment }}</p>
</ion-label>
</ion-item>
} @empty {
<ion-item lines="none">
<ion-label class="ion-text-center">
<p>No comments yet</p>
</ion-label>
</ion-item>
}
</ion-content>
<ion-item lines="none">
<ion-input #commentInput placeholder="Add a comment..."></ion-input>
<ion-button (click)="comment(commentInput)" fill="clear" slot="end">
<ion-icon name="send" slot="icon-only"></ion-icon>
</ion-button>
</ion-item>
</ng-template>
</ion-modal>
class="comments"
>
<ng-template>
<ion-item lines="full">
<ion-label class="ion-text-center">
<h2>Comments</h2>
</ion-label>
</ion-item>
<ion-content>
@for (comment of locationRiddleState.locationRiddle()!.comments; track comment.username) {
<ion-item lines="none">
<ion-label class="ion-text-wrap" (click)="router.navigate(['/home/profiles/' + comment.username])">
<h3 class="grey-text">{{ comment.username }}</h3>
<p>{{ comment.comment }}</p>
</ion-label>
</ion-item>
} @empty {
<ion-item lines="none">
<ion-label class="ion-text-center">
<p>No comments yet</p>
</ion-label>
</ion-item>
}
</ion-content>
<ion-item lines="none">
<ion-input #commentInput placeholder="Add a comment..."></ion-input>
<ion-button (click)="comment(commentInput)" fill="clear" slot="end">
<ion-icon name="send" slot="icon-only"></ion-icon>
</ion-button>
</ion-item>
</ng-template>
</ion-modal>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
ion-item {
--background: transparent;
--color: white;
}

ion-card-header {
Expand All @@ -10,3 +9,7 @@ ion-card-header {
::ng-deep .comments > .ion-page {
height: 50% !important;
}

.grey-text {
color: grey;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CommonModule } from '@angular/common';
import { Component, effect, inject, input, output } from '@angular/core';
import { Router } from '@angular/router';
import {
IonAvatar,
IonButton,
Expand All @@ -16,15 +15,16 @@ import {
IonInput,
IonItem,
IonLabel,
IonModal
IonModal,
IonAlert
} from '@ionic/angular/standalone';
import { Coordinate } from 'ol/coordinate';
import { LocationRiddle } from 'src/app/model/location-riddle';
import { LocationRiddleApiService } from 'src/app/services/api/location-riddle-api.service';
import { MapComponent } from 'src/app/shared/map/map.component';
import { LocationRiddleStateService } from './data-access/location-riddle-state.service';
import { RatingComponent } from './ui/rating/rating.component';

import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-location-riddle-post',
templateUrl: './location-riddle-post.component.html',
Expand All @@ -46,7 +46,8 @@ import { RatingComponent } from './ui/rating/rating.component';
IonButton,
CommonModule,
MapComponent,
RatingComponent
RatingComponent,
IonAlert
],
styleUrls: ['./location-riddle-post.component.scss'],
providers: [LocationRiddleStateService, LocationRiddleApiService],
Expand All @@ -55,14 +56,29 @@ import { RatingComponent } from './ui/rating/rating.component';
export class LocationRiddlePostComponent {
// Services
locationRiddleState = inject(LocationRiddleStateService);
router = inject(Router);

// Input/Output
locationRiddle = input.required<LocationRiddle>();
username = input.required<string>();

submitGuess = output<Coordinate>();

router = inject(Router);
route = inject(ActivatedRoute);

public alertButtons = [
{
text: 'Cancel',
role: 'cancel'
},
{
text: 'Delete',
role: 'confirm',
handler: () => {
this.locationRiddleState.deleteLocationRiddle.next(this.locationRiddle().locationRiddleId);
}
}
];
// Variables
commentsModalOpen = false;

Expand Down Expand Up @@ -107,4 +123,8 @@ export class LocationRiddlePostComponent {
this.locationRiddleState.submittedGuess.next();
}
}

isCurrentUser(): boolean {
return this.locationRiddle().username === this.username() && !this.route.snapshot.params['username'];
}
}

0 comments on commit f58014b

Please sign in to comment.