Skip to content

Commit

Permalink
Merge branch 'master' into dev
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/app/services/user-notification.service.ts
#	src/app/utilities/mkj-user-notifications/mkj-user-notifications.component.html
  • Loading branch information
Sams, Roland authored and Sams, Roland committed Nov 29, 2024
2 parents 58ecf70 + a19eb18 commit a1dcdd3
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ export class TerminDetailsComponent implements OnInit {

ngOnInit(): void {
this.route.params.subscribe((e) => {
this.loading = true;
this.termin = null;
this.termineApiService.getById(e.id).subscribe(
(ausrueckung) => {
this.termin = ausrueckung;
this.updateToolbarButtons();
this.getGespielteNoten();
this.loading = false;
},
(error) => this.infoService.error(error),
() => (this.loading = false)
Expand Down
14 changes: 9 additions & 5 deletions src/app/helpers/util-functions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { MkjDropdownOption } from "../utilities/form-input-components/mkj-dropdown/mkj-dropdown.component";
import {
MkjDropdownOption
} from '../utilities/form-input-components/mkj-dropdown/mkj-dropdown.component';

export abstract class UtilFunctions {
public static getDropdownOptionsFromEnum(
enumObject: any
): MkjDropdownOption[] {
return Object.entries(enumObject).map((entry) => {
return { label: entry[0], value: entry[1] };
return {label: entry[0], value: entry[1]};
});
}

Expand All @@ -25,8 +27,8 @@ export abstract class UtilFunctions {
}

public static generateRandomHexColor(): string {
let color = "#";
const possible = "0123456789ABCDEF";
let color = '#';
const possible = '0123456789ABCDEF';

for (let i = 0; i < 6; i++) {
color += possible.charAt(
Expand All @@ -38,7 +40,9 @@ export abstract class UtilFunctions {
}

public static isDarkBackground(backgroundColor: string): boolean {
if (!backgroundColor) return false;
if (!backgroundColor) {
return false;
}
const color = backgroundColor.substring(1); // remove the leading '#'
const r = parseInt(color.substring(0, 2), 16);
const g = parseInt(color.substring(2, 4), 16);
Expand Down
255 changes: 136 additions & 119 deletions src/app/services/user-notification.service.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
import { Injectable } from '@angular/core';
import { UserNotificationAPIService } from './api/user-notifications-api.service';
import { WebsocketService } from './api/websocket.service';
import { SubSink } from 'subsink';
import { UserNotification, UserNotificationType } from '../models/User-Notifications';
import { BehaviorSubject, map } from 'rxjs';
import { MenuItem } from 'primeng/api';
import { Termin } from '../models/Termin';
import { DatePipe } from '@angular/common';
import { Router } from '@angular/router';
import {Injectable} from '@angular/core';
import {UserNotificationAPIService} from './api/user-notifications-api.service';
import {WebsocketService} from './api/websocket.service';
import {SubSink} from 'subsink';
import {UserNotification, UserNotificationType} from '../models/User-Notifications';
import {BehaviorSubject, map} from 'rxjs';
import {MenuItem} from 'primeng/api';
import {Termin} from '../models/Termin';
import {DatePipe} from '@angular/common';
import {Router} from '@angular/router';
import dayjs from 'dayjs';

@Injectable({
providedIn: 'root',
providedIn: 'root',
})
export class UserNotificationService {
private _userNotifications = new BehaviorSubject<UserNotification[]>([]);
private _subs = new SubSink();

public readonly userNotifications = this._userNotifications
.asObservable()
.pipe(map((notifications) => notifications.map((n) => this.mapNotificationToMenuItem(n))));

constructor(
private apiService: UserNotificationAPIService,
private datePipe: DatePipe,
private router: Router,
webSocketService: WebsocketService
) {
this._subs.add(
webSocketService.getUserNotificationsChannel().subscribe((notification: UserNotification) => {
this.insertNotification(notification);
})
);
}
private _userNotifications = new BehaviorSubject<UserNotification[]>([]);
private _subs = new SubSink();

public readonly userNotifications = this._userNotifications
.asObservable()
.pipe(map((notifications) => notifications.map((n) => this.mapNotificationToMenuItem(n))));

constructor(
private apiService: UserNotificationAPIService,
private datePipe: DatePipe,
private router: Router,
webSocketService: WebsocketService
) {
this.getUnreadNotifications();

this._subs.add(
webSocketService.getUserNotificationsChannel().subscribe((notification: UserNotification) => {
this.insertNotification(notification);
})
);
}

public updateUnreadNotifications(): void {
this.apiService.getUnreadNotifications().subscribe((notifications) => {
Expand All @@ -43,104 +45,119 @@ export class UserNotificationService {
public addNotification(notification: UserNotification): void {
this.insertNotification(notification);
}
public markAllAsRead(): void {
this.apiService.markAllAsRead().subscribe();
}

private insertNotification(notification: UserNotification): void {
if (notification.created_at == null) {
notification.created_at = dayjs().format('YYYY-MM-DD HH:mm:ss');
public addNotification(notification: UserNotification): void {
this.insertNotification(notification);
}
const notifications = this._userNotifications.value;
notifications.push(notification);
notifications.sort((a, b) => {
if (a.created_at < b.created_at) {
return 1;
}
if (a.created_at > b.created_at) {
return -1;
}
return 0;
});
this._userNotifications.next(notifications);
}

private markAsRead(notification: UserNotification): void {
console.log(notification);
if (notification.type === UserNotificationType.SwUpdate || notification.type === UserNotificationType.TestSocket) {
this.removeNotification(notification);
return;
private insertNotification(notification: UserNotification): void {
if (notification.created_at == null) {
notification.created_at = dayjs().format('YYYY-MM-DD HH:mm:ss');
}
const notifications = this._userNotifications.value;
notifications.push(notification);
notifications.sort((a, b) => {
if (a.created_at < b.created_at) {
return 1;
}
if (a.created_at > b.created_at) {
return -1;
}
return 0;
});
this._userNotifications.next(notifications);
}
this.apiService.markAsRead(notification.id).subscribe(() => {
this.removeNotification(notification);
});
}

private removeNotification(notification: UserNotification): void {
const notifications = this._userNotifications.value.filter((n) => n.id !== notification.id);
this._userNotifications.next(notifications);
}
private getUnreadNotifications(): void {
this.apiService.getUnreadNotifications().subscribe((notifications) => {
this._userNotifications.next(notifications);
});
}

private mapNotificationToMenuItem(notification: UserNotification): MenuItem {
return {
label: this.getLabel(notification),
subLabel: this.getSubLabel(notification),
icon: this.getIcon(notification),
command: () => {
this.markAsRead(notification);
this.invokeCommand(notification);
},
unread: notification.read_at == null,
notification: notification,
};
}
private markAsRead(notification: UserNotification): void {
if (notification.type === UserNotificationType.SwUpdate || notification.type === UserNotificationType.TestSocket) {
this.removeNotification(notification);
return;
}
this.apiService.markAsRead(notification.id).subscribe(() => {
this.removeNotification(notification);
});
}

private getLabel(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
return 'Neuer Termin';
case UserNotificationType.TerminUpdated:
return 'Termin aktualisiert';
case UserNotificationType.SwUpdate:
return 'Update verfügbar';
default:
return 'Sonstiges';
private removeNotification(notification: UserNotification): void {
const notifications = this._userNotifications.value.filter((n) => n.id !== notification.id);
this._userNotifications.next(notifications);
}
}
private getSubLabel(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
const termin = notification.data as Termin;
return termin?.name + ' ' + this.datePipe.transform(termin.vonDatum, 'd. MMMM YYYY');
case UserNotificationType.TerminUpdated:
const updatedTermin = notification.data as Termin;
return updatedTermin?.name + ' ' + this.datePipe.transform(updatedTermin.vonDatum, 'd. MMMM YYYY');
case UserNotificationType.SwUpdate:
return 'Installieren?';
default:
return 'Sonstiges';

private mapNotificationToMenuItem(notification: UserNotification): MenuItem {
return {
label: this.getLabel(notification),
subLabel: this.getSubLabel(notification),
icon: this.getIcon(notification),
command: () => {
this.markAsRead(notification);
this.invokeCommand(notification);
},
unread: notification.read_at == null,
notification: notification,
};
}
}
private getIcon(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
return 'pi pi-calendar-plus';
case UserNotificationType.TerminUpdated:
return 'pi pi-refresh';
case UserNotificationType.SwUpdate:
return 'pi pi-exclamation-triangle';
default:
return '';

private getLabel(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
return 'Neuer Termin';
case UserNotificationType.TerminUpdated:
return 'Termin aktualisiert';
case UserNotificationType.SwUpdate:
return 'Update verfügbar';
default:
return 'Sonstiges';
}
}
}
private invokeCommand(notification: UserNotification): void {
switch (notification.type) {
case UserNotificationType.TerminUpdated:
case UserNotificationType.TerminCreated:
this.router.navigate(['/termine/details', (notification.data as Termin).id]);
return;
case UserNotificationType.SwUpdate:
notification.command();
return;
default:
return void 0;

private getSubLabel(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
const termin = notification.data as Termin;
return termin?.name + ' ' + this.datePipe.transform(termin.vonDatum, 'd. MMMM YYYY');
case UserNotificationType.TerminUpdated:
const updatedTermin = notification.data as Termin;
return updatedTermin?.name + ' ' + this.datePipe.transform(updatedTermin.vonDatum, 'd. MMMM YYYY');
case UserNotificationType.SwUpdate:
return 'Installieren?';
default:
return 'Sonstiges';
}
}

private getIcon(notification: UserNotification): string {
switch (notification.type) {
case UserNotificationType.TerminCreated:
return 'pi pi-calendar-plus';
case UserNotificationType.TerminUpdated:
return 'pi pi-refresh';
case UserNotificationType.SwUpdate:
return 'pi pi-exclamation-triangle';
default:
return '';
}
}

private invokeCommand(notification: UserNotification): void {
switch (notification.type) {
case UserNotificationType.TerminUpdated:
case UserNotificationType.TerminCreated:
this.router.navigate(['/termine/details', (notification.data as Termin).id]);
return;
case UserNotificationType.SwUpdate:
notification.command();
return;
default:
return void 0;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
<i
pBadge
class="pi pi-bell text-2xl cursor-pointer"
style="color: var(--primary-color-text)"
severity="warning"
[value]="this.menuItems.length"
[class.p-disabled]="this.menuItems.length === 0"
[class.hide-badge]="this.menuItems.length === 0"
(click)="this.menuItems.length ? notificationmenu.toggle($event) : null"
pBadge
class="pi pi-bell text-2xl cursor-pointer text-white"
severity="warning"
[value]="this.menuItems.length"
[class.p-disabled]="this.menuItems.length === 0"
[class.hide-badge]="this.menuItems.length === 0"
(click)="this.menuItems.length ? notificationmenu.toggle($event) : null"
></i>

<p-menu #notificationmenu [model]="menuItems" [popup]="true">
<ng-template pTemplate="item" let-item>
<div class="flex flex-column gap-2 p-menuitem-link flex align-items-center p-1">
<div class="flex gap-2">
<i [class]="item.icon"></i><span>{{ item.label }}</span>
</div>
<div class="opacity-80">{{ item.subLabel }}</div>
<div></div>

<p-overlayPanel #notificationmenu>
<div class="mb-3">
<button pButton label="Alle gelesen" icon="mdi mdi-email-open-outline"
class="p-button-outlined p-button-sm" (click)="markAllAsRead(); notificationmenu.hide()"></button>
</div>
</ng-template>
</p-menu>
<p-menu [model]="menuItems" [popup]="false">
<ng-template pTemplate="item" let-item>
<div class="flex flex-column gap-2 p-menuitem-link align-items-center">
<div class="flex gap-2">
<i [class]="item.icon"></i><span>{{ item.label }}</span>
</div>
<div class="opacity-80">{{ item.subLabel }}</div>
<div></div>
</div>
</ng-template>
</p-menu>
</p-overlayPanel>
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ export class MkjUserNotificationsComponent implements OnInit, OnDestroy {
public ngOnDestroy(): void {
this._subs.unsubscribe();
}

public markAllAsRead(): void {
this.userNotificationsService.markAllAsRead();
this.menuItems = [];
}
}

0 comments on commit a1dcdd3

Please sign in to comment.