Skip to content

Commit

Permalink
Introduce Notification Center (#2469)
Browse files Browse the repository at this point in the history
* create notification center

* add clock & backdrop

* introduce new notification model

* fix notification stack

* notification center done

* clean standby service and create swipe function

* add setting to hide carret

* remove test notifications
  • Loading branch information
UnchartedBull committed Dec 2, 2021
1 parent a030d63 commit f479524
Show file tree
Hide file tree
Showing 36 changed files with 858 additions and 430 deletions.
7 changes: 6 additions & 1 deletion helper/config.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const configSchema = {
'preheatButton',
'printTimeGenius',
'psuControl',
'ophom',
'ophom',
'tpLinkSmartPlug',
'tasmota',
'tasmotaMqtt',
Expand Down Expand Up @@ -294,6 +294,7 @@ const configSchema = {
'screenSleepCommand',
'screenWakeupCommand',
'showExtruderControl',
'showNotificationCenterIcon',
],
properties: {
customActions: {
Expand Down Expand Up @@ -404,6 +405,10 @@ const configSchema = {
$id: '#/properties/octodash/properties/showExtruderControl',
type: 'boolean',
},
showNotificationCenterIcon: {
$id: '#/properties/octodash/properties/showNotificationCenterIcon',
type: 'boolean',
},
},
},
},
Expand Down
44 changes: 37 additions & 7 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
<app-notification></app-notification>
<div class="container">
<app-notification-center
*ngIf="initialized"
[ngStyle]="{ top: notificationCenterTop }"
(setNotificationCenterAnimation)="setNotificationCenterAnimation($event)"
(setNotificationCenterPosition)="setNotificationCenterPosition($event)"
[ngClass]="{ animateNotificationCenter: animateNotificationCenter }"></app-notification-center>
<div
class="container"
(touchstart)="onTouchStart($event)"
(touchmove)="onTouchMove($event)"
(touchend)="onTouchEnd($event)">
<div *ngIf="!activated">
<img src="assets/icon/icon-main.svg" class="splash-screen__icon-small" />
<span class="splash-screen__text"
[ngClass]="{ 'loading-dots': status === 'connecting' || status === 'initializing' }">{{ status }}</span>
<span
class="splash-screen__text"
[ngClass]="{ 'loading-dots': status === 'connecting' || status === 'initializing' }"
>{{ status }}</span
>
<span class="splash-screen__hint" *ngIf="showConnectionHint" i18n="@@long-init">
Initializing is taking longer than usual. <br />
Please make sure that OctoPrint is running and that CORS is enabled for the API.
</span>
</div>

<fa-icon
*ngIf="initialized && showNotificationCenterIcon()"
[icon]="['fas', 'caret-down']"
(click)="showNotificationCenter()"
matRipple
[matRippleUnbounded]="true"
[matRippleCentered]="true"></fa-icon>
<router-outlet (activate)="activated = true"></router-outlet>
</div>

<ng-container *ngIf="!loadingAnimationCached">
<ng-lottie id="loadingAnimationCache" [hidden]="true" [options]="loadingOptionsCache"
<ng-lottie
id="loadingAnimationCache"
[hidden]="true"
[options]="loadingOptionsCache"
(dataReady)="loadingAnimationCacheDone()"></ng-lottie>
</ng-container>

<ng-container *ngIf="!checkmarkAnimationCached">
<ng-lottie id="checkmarkAnimationCache" [hidden]="true" [options]="checkmarkOptionsCache"
<ng-lottie
id="checkmarkAnimationCache"
[hidden]="true"
[options]="checkmarkOptionsCache"
(dataReady)="checkmarkAnimationCacheDone()"></ng-lottie>
</ng-container>

<ng-container *ngIf="!toggleSwitchAnimationCached">
<ng-lottie id="toggleSwitchAnimationCache" [hidden]="true" [options]="toggleSwitchOptionsCache"
<ng-lottie
id="toggleSwitchAnimationCache"
[hidden]="true"
[options]="toggleSwitchOptionsCache"
(dataReady)="toggleSwitchAnimationCacheDone()"></ng-lottie>
</ng-container>
</ng-container>
11 changes: 11 additions & 0 deletions src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,14 @@
display: block;
position: relative;
}

fa-icon {
position: fixed;
left: calc(50% - 10px);
top: -8px;
opacity: 0.4;
}

.animateNotificationCenter {
transition: top 0.3s ease-in-out;
}
52 changes: 52 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import { SocketService } from './services/socket/socket.service';
})
export class AppComponent implements OnInit {
public activated = false;
public initialized = false;
public status = $localize`:@@initializing:initializing`;
public showConnectionHint = false;
public notificationCenterTop = '-100%';
public animateNotificationCenter = true;
public startSwipe: Touch | undefined;

public loadingOptionsCache: AnimationOptions = {
path: 'assets/animations/loading.json',
Expand Down Expand Up @@ -83,6 +87,7 @@ export class AppComponent implements OnInit {
} else {
this.router.navigate(['/main-screen-no-touch']);
}
this.initialized = true;
})
.finally(() => clearTimeout(showPrinterConnectedTimeout));
}
Expand All @@ -95,7 +100,54 @@ export class AppComponent implements OnInit {
this.checkmarkAnimationCached = true;
}

public showNotificationCenterIcon(): boolean {
return this.configService.showNotificationCenterIcon();
}

public toggleSwitchAnimationCacheDone(): void {
this.toggleSwitchAnimationCached = true;
}

public hideNotificationCenter() {
this.notificationCenterTop = '-100%';
}

public showNotificationCenter() {
this.notificationCenterTop = '0%';
}

public setNotificationCenterPosition(position: string) {
this.notificationCenterTop = position;
}

public setNotificationCenterAnimation(enabled: boolean) {
this.animateNotificationCenter = enabled;
}

public onTouchStart(event: TouchEvent) {
if (event.changedTouches[0].clientY < event.view.innerHeight / 8) {
this.startSwipe = event.changedTouches[0];
this.animateNotificationCenter = false;
} else {
this.startSwipe = undefined;
}
}

public onTouchMove(event: TouchEvent) {
if (this.startSwipe) {
this.notificationCenterTop = `${(100 - (event.changedTouches[0].clientY / event.view.innerHeight) * 100) * -1}%`;
}
}

public onTouchEnd(event: TouchEvent) {
if (this.startSwipe) {
this.animateNotificationCenter = true;
const endSwipe = event.changedTouches[0];
if (endSwipe.clientY > event.view.innerHeight / 3) {
this.showNotificationCenter();
} else {
this.hideNotificationCenter();
}
}
}
}
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { MainScreenComponent } from './main-screen/main-screen.component';
import { MainScreenNoTouchComponent } from './main-screen/no-touch/main-screen-no-touch.component';
import { NotificationComponent } from './notification/notification.component';
import { NotificationService } from './notification/notification.service';
import { NotificationCenterComponent } from './notification-center/notification-center.component';
import { PrintControlComponent } from './print-control/print-control.component';
import { PrinterStatusComponent } from './printer-status/printer-status.component';
import { EnclosureOctoprintService } from './services/enclosure/enclosure.octoprint.service';
Expand Down Expand Up @@ -103,6 +104,7 @@ export function playerFactory(): LottiePlayer {
PurgeFilamentComponent,
CustomActionsComponent,
ToggleSwitchComponent,
NotificationCenterComponent,
],
imports: [
AppRoutingModule,
Expand Down
10 changes: 9 additions & 1 deletion src/app/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import _ from 'lodash-es';
import { Config } from './config/config.model';
import { ConfigService } from './config/config.service';
import { ElectronService } from './electron.service';
import { NotificationType } from './model';
import { NotificationService } from './notification/notification.service';

@Injectable()
Expand Down Expand Up @@ -54,6 +55,8 @@ export class AppService {
"/plugins must have required property 'spoolManager'": config =>
(config.plugins.spoolManager = { enabled: false }),
"/plugins must have required property 'ophom'": config => (config.plugins.ophom = { enabled: false }),
"/octodash must have required property 'showNotificationCenterIcon'": config =>
(config.octodash.showNotificationCenterIcon = true),
};
}

Expand Down Expand Up @@ -107,7 +110,12 @@ export class AppService {
});

this.electronService.on('customStylesError', (_, customCSSError: string): void => {
this.notificationService.setError($localize`:@@error-load-style:Can't load custom styles!`, customCSSError);
this.notificationService.setNotification({
heading: $localize`:@@error-load-style:Can't load custom styles!`,
text: customCSSError,
type: NotificationType.ERROR,
time: new Date(),
});
});
}

Expand Down
12 changes: 7 additions & 5 deletions src/app/bottom-bar/bottom-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Component, OnDestroy } from '@angular/core';
import { Subscription, timer } from 'rxjs';

import { ConfigService } from '../config/config.service';
import { PrinterState, PrinterStatus, TemperatureReading } from '../model';
import { NotificationType, PrinterState, PrinterStatus, TemperatureReading } from '../model';
import { NotificationService } from '../notification/notification.service';
import { EnclosureService } from '../services/enclosure/enclosure.service';
import { SocketService } from '../services/socket/socket.service';
Expand Down Expand Up @@ -34,10 +34,12 @@ export class BottomBarComponent implements OnDestroy {
this.enclosureService.getEnclosureTemperature().subscribe({
next: (temperatureReading: TemperatureReading) => (this.enclosureTemperature = temperatureReading),
error: (error: HttpErrorResponse) => {
this.notificationService.setError(
$localize`:@@error-enclosure-temp:Can't retrieve enclosure temperature!`,
error.message,
);
this.notificationService.setNotification({
heading: $localize`:@@error-enclosure-temp:Can't retrieve enclosure temperature!`,
text: error.message,
type: NotificationType.ERROR,
time: new Date(),
});
},
});
}
Expand Down
1 change: 1 addition & 0 deletions src/app/config/config.default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,6 @@ export const defaultConfig: Config = {
screenSleepCommand: 'xset dpms force standby',
screenWakeupCommand: 'xset s off && xset -dpms && xset s noblank',
showExtruderControl: true,
showNotificationCenterIcon: true,
},
};
1 change: 1 addition & 0 deletions src/app/config/config.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ interface OctoDash {
screenSleepCommand: string;
screenWakeupCommand: string;
showExtruderControl: boolean;
showNotificationCenterIcon: boolean;
}

export interface CustomAction {
Expand Down
16 changes: 12 additions & 4 deletions src/app/config/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Injectable, NgZone } from '@angular/core';
import _ from 'lodash-es';

import { ElectronService } from '../electron.service';
import { NotificationType } from '../model';
import { NotificationService } from '../notification/notification.service';
import { Config, CustomAction, HttpHeader, URLSplit } from './config.model';

Expand All @@ -26,10 +27,13 @@ export class ConfigService {
this.electronService.on('configRead', (_, config: Config) => this.initialize(config));
this.electronService.on('configSaved', (_, config: Config) => this.initialize(config));
this.electronService.on('configError', (_, error: string) => {
this.notificationService.setError(
error,
$localize`:@@error-restart:Please restart your system. If the issue persists open an issue on GitHub.`,
);
this.notificationService.setNotification({
heading: error,
text: $localize`:@@error-restart:Please restart your system. If the issue persists open an issue on GitHub.`,
type: NotificationType.ERROR,
time: new Date(),
sticky: true,
});
});

this.electronService.on('configPass', () => {
Expand Down Expand Up @@ -330,4 +334,8 @@ export class ConfigService {
this.config.octodash.fileSorting.order = order;
this.saveConfig(this.config);
}

public showNotificationCenterIcon(): boolean {
return this.config.octodash.showNotificationCenterIcon;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpErrorResponse, HttpResponse, HttpResponseBase } from '@angular/common/http';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { interval } from 'rxjs';
import { NotificationType } from 'src/app/model';

import { TokenSuccess } from '../../../model/octoprint/auth.model';
import { NotificationService } from '../../../notification/notification.service';
Expand Down Expand Up @@ -29,39 +30,45 @@ export class OctoprintAuthenticationComponent {
},
error: (error: HttpErrorResponse) => {
if (error.status === 0) {
this.notificationService.setError(
$localize`:@@octoprint-connection-failed:Can't connect to OctoPrint!`,
$localize`:@@octoprint-connection-failed-message:Check the URL/IP and make sure that your OctoPrint instance is reachable from this device.`,
);
this.notificationService.setNotification({
heading: $localize`:@@octoprint-connection-failed:Can't connect to OctoPrint!`,
text: $localize`:@@octoprint-connection-failed-message:Check the URL/IP and make sure that your OctoPrint instance is reachable from this device.`,
type: NotificationType.ERROR,
time: new Date(),
});
} else this.setAutologinWarning();
},
});
}

private setAutologinWarning(): void {
this.notificationService.setWarning(
$localize`:@@unsupported-autologin:Automatic login not supported!`,
$localize`:@@manually-create-api-key:Please create the API Key manually and paste it in the bottom field.`,
);
this.notificationService.setNotification({
heading: $localize`:@@unsupported-autologin:Automatic login not supported!`,
text: $localize`:@@manually-create-api-key:Please create the API Key manually and paste it in the bottom field.`,
type: NotificationType.WARN,
time: new Date(),
});
}

private sendLoginRequest(): void {
this.authService.startAuthProcess(this.octoprintURL).subscribe({
next: (token: string) => {
this.notificationService.setNotification(
$localize`:@@login-request-sent:Login request send!`,
$localize`:@@login-request-sent-message:Please confirm the request via the popup in the OctoPrint WebUI.`,
);
this.notificationService.setNotification({
heading: $localize`:@@login-request-sent:Login request send!`,
text: $localize`:@@login-request-sent-message:Please confirm the request via the popup in the OctoPrint WebUI.`,
type: NotificationType.INFO,
time: new Date(),
});
setTimeout(() => {
this.notificationService.closeNotification();
}, 10 * 1000);
this.pollResult(token);
},
error: () => this.setAutologinWarning(),
});
}

private pollResult(token: string): void {
setTimeout(() => {
this.notificationService.closeNotification();
}, 2000);
const pollInterval = interval(1000).subscribe(() => {
this.authService.pollAuthProcessStatus(this.octoprintURL, token).subscribe({
next: (result: HttpResponse<TokenSuccess>) => {
Expand Down
Loading

0 comments on commit f479524

Please sign in to comment.