Migrate Angular UI from portmaster-ui to desktop/angular. Update Earthfile to build libs, UI and tauri-builtin

This commit is contained in:
Patrick Pacher
2024-03-20 10:43:29 +01:00
parent 66381baa1a
commit 4b77945517
922 changed files with 84071 additions and 26 deletions

View File

@@ -0,0 +1,230 @@
<div class="flex flex-col items-center gap-1">
<div class="relative w-16 h-16">
<app-security-lock [@fadeIn] [@fadeOut] mode="small" class="absolute w-16 h-16" routerLink="dashboard"
*ngIf="sideDashStatus === 'collapsed'">
</app-security-lock>
<div class="absolute flex flex-col items-center justify-center w-16 h-16 outline-none" routerLink="dashboard"
[@fadeIn] [@fadeOut] *ngIf="sideDashStatus === 'expanded'">
<div class="relative">
<svg [class.connected]="(connected$ | async)" data-name="Layer 1" viewBox="0 0 128 128"
class="spin reverse logo-image">
<g data-name="Main" fill-rule="evenodd">
<path shape-rendering="geometricPrecision" fill="#fff" class="inner"
d="M176.11 36.73l-5-8.61a41.53 41.53 0 00-14.73 57.22l8.55-5.12a31.58 31.58 0 0111.19-43.49z"
transform="translate(-127.99 .01)" style="isolation:isolate" opacity=".8"></path>
<path shape-rendering="geometricPrecision" fill="#fff" class="inner"
d="M222.36 72.63a31.55 31.55 0 01-45 19.35l-4.62 8.84a41.54 41.54 0 0059.17-25.46z"
transform="translate(-127.99 .01)" style="isolation:isolate" opacity=".8"></path>
</g>
</svg>
<svg [class.connected]="(connected$ | async)" data-name="Layer 1" viewBox="0 0 128 128" class="spin logo-image">
<g data-name="Main" fill-rule="evenodd">
<path shape-rendering="geometricPrecision" fill="#fff" class="inner reverse"
d="M197 83a19.66 19.66 0 01-19.25-32.57l-4.5-4.27A25.87 25.87 0 00198.59 89z"
transform="translate(-127.99 .01)" style="isolation:isolate" opacity=".6"></path>
</g>
</svg>
<svg [class.connected]="(connected$ | async)" data-name="Layer 1" viewBox="0 0 128 128" class="logo-image">
<g data-name="Main" fill-rule="evenodd">
<path shape-rendering="geometricPrecision" fill="#fff"
d="M192 112.64A48.64 48.64 0 11240.64 64 48.64 48.64 0 01192 112.64zM256 64a64 64 0 10-64 64 64 64 0 0064-64z"
transform="translate(-127.99 .1)"></path>
</g>
</svg>
</div>
</div>
</div>
<div class="flex justify-center">
<sfng-tipup key="intro"></sfng-tipup>
</div>
<div class="nav-list">
<div class="relative link" (click)="toggleSideDash($event)">
<svg [class.-rotate-180]="sideDashStatus === 'expanded'" [class.bg-gray-400]="sideDashStatus === 'collapsed'"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
class="w-5 h-5 transition-all duration-200 rounded">
<path fill-rule="evenodd"
d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
clip-rule="evenodd" />
</svg>
</div>
</div>
<div class="pt-1 border-t border-gray-400 nav-list">
<!-- The notification drop-down -->
<div sfngTipUpTrigger="navNotifications" sfngTipUpPassive class="relative mt-3" (click)="toggleSideDash($event)"
*ngIf="sideDashStatus !== 'expanded'">
<svg class="w-4 h-4 {{ hasNewNotifications ? notificationColor : 'text-tertiary' }}" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</div>
<!-- The prompt list drop-down -->
<div class="relative link" cdkOverlayOrigin #promptOrigin="cdkOverlayOrigin"
*ngIf="hasNewPrompts || globalPromptingEnabled" (click)="promptDropDown.toggle(promptOrigin)"
[class.active]="promptDropDown.isOpen">
<span *ngIf="hasNewPrompts" class="absolute w-1.5 h-1.5 bg-yellow-300 rounded-full top-1.5 right-1.5"></span>
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<sfng-dropdown [positions]="dropDownPositions" externalTrigger="true" #promptDropDown offsetY="0" offsetX="10"
overlayClass="rounded-t">
<app-prompt-list></app-prompt-list>
</sfng-dropdown>
</div>
</div>
<div class="nav-list">
<!-- Network Activity -->
<div sfng-tooltip="Network Activity" sfngTooltipDelay="1000" snfgTooltipPosition="right" routerLinkActive="active"
routerLink="monitor" class="link" sfngTipUpTrigger="navMonitor" sfngTipUpPassive>
<svg viewBox="0 0 24 24" class="monitor">
<g fill="none">
<path shape-rendering="geometricPrecision" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8.464 8.464c-1.953 1.953-1.953 5.118 0 7.071 1.953 1.953 5.118 1.953 7.071 0 1.953-1.953 1.953-5.119 0-7.071C14.559 7.488 13.28 7 12 7" />
<path shape-rendering="geometricPrecision" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5.636 5.636c-3.515 3.515-3.515 9.213 0 12.728 3.515 3.515 9.213 3.515 12.728 0 3.515-3.515 3.515-9.213 0-12.728-2.627-2.627-6.474-3.289-9.717-1.989M5.64 5.64L12 12" />
</g>
</svg>
</div>
<!-- App View -->
<div sfng-tooltip="Apps and Profiles" sfngTooltipDelay="1000" snfgTooltipPosition="right" routerLinkActive="active"
routerLink="app" class="link" sfngTipUpTrigger="navApps" sfngTipUpPassive>
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 24 24" class="app" fill="none">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" fill="currentColor"
d="M19 21h-3a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2Z" />
<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9h-3a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2ZM5 3h3a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2ZM5 15h3a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2Z" />
</svg>
</div>
<!-- SPN -->
<div sfng-tooltip="Safing Privacy Network" sfngTooltipDelay="1000" snfgTooltipPosition="right"
routerLinkActive="active" routerLink="spn" class="link" sfngTipUpTrigger="navMap" sfngTipUpPassive>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="spn" stroke="currentColor">
<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path
d="M6.488 15.581c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.048-.781 2.83 0M13.415 3.586c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.049-.781 2.83 0M20.343 15.58c.782.781.782 2.048 0 2.829-.782.781-2.049.781-2.83 0-.782-.781-.782-2.048 0-2.829.781-.781 2.048-.781 2.83 0" />
<path
d="M17.721 18.581C16.269 20.071 14.246 21 12 21c-1.146 0-2.231-.246-3.215-.68M4.293 15.152c-.56-1.999-.352-4.21.769-6.151.574-.995 1.334-1.814 2.205-2.449M13.975 5.254c2.017.512 3.834 1.799 4.957 3.743.569.985.899 2.041 1.018 3.103" />
</g>
</svg>
</div>
<!-- Global Settings -->
<div sfng-tooltip="Global Settings" sfngTooltipDelay="1000" snfgTooltipPosition="right" routerLinkActive="active"
routerLink="settings" class="link" sfngTipUpTrigger="navSettings" sfngTipUpPassive>
<svg viewBox="0 0 24 24" class="settings">
<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path shape-rendering="geometricPrecision"
d="M13.7678 10.2322c.976311.976311.976311 2.55922 0 3.53553-.976311.976311-2.55922.976311-3.53553 0-.976311-.976311-.976311-2.55922 0-3.53553.976311-.976311 2.55922-.976311 3.53553 0" />
<path shape-rendering="geometricPrecision"
d="M14.849 4.12l.583.194c.534.178.895.678.895 1.241v.837c0 .712.568 1.293 1.28 1.308l.838.018c.485.01.925.289 1.142.723l.275.55c.252.504.153 1.112-.245 1.51l-.592.592c-.503.503-.512 1.316-.02 1.83l.58.606c.336.351.45.858.296 1.319l-.194.583c-.178.534-.678.895-1.241.895h-.837c-.712 0-1.293.568-1.308 1.28l-.018.838c-.01.485-.289.925-.723 1.142l-.55.275c-.504.252-1.112.153-1.51-.245l-.592-.592c-.503-.503-1.316-.512-1.83-.02l-.606.58c-.351.336-.858.45-1.319.296l-.583-.194c-.534-.178-.895-.678-.895-1.241v-.837c0-.712-.568-1.293-1.28-1.308l-.838-.018c-.485-.01-.925-.289-1.142-.723l-.275-.55c-.252-.504-.153-1.112.245-1.51l.592-.592c.503-.503.512-1.316.02-1.83l-.58-.606c-.337-.352-.451-.86-.297-1.32l.194-.583c.178-.534.678-.895 1.241-.895h.837c.712 0 1.293-.568 1.308-1.28l.018-.838c.012-.485.29-.925.724-1.142l.55-.275c.504-.252 1.112-.153 1.51.245l.592.592c.503.503 1.316.512 1.83.02l.606-.58c.351-.335.859-.449 1.319-.295z" />
</g>
</svg>
</div>
<div class="w-full border-t border-gray-400"></div>
<div sfng-tooltip="Get Help" sfngTooltipDelay="1000" snfgTooltipPosition="right" routerLink="support"
routerLinkActive="active" class="link" sfngTipUpTrigger="navSupport" sfngTipUpPassive>
<svg viewBox="0 0 24 24" class="help">
<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path shape-rendering="geometricPrecision"
d="M12 21v0c-4.971 0-9-4.029-9-9v0c0-4.971 4.029-9 9-9v0c4.971 0 9 4.029 9 9v0c0 4.971-4.029 9-9 9z" />
<path shape-rendering="geometricPrecision"
d="M12 13.25V13c0-.817.505-1.26 1.011-1.6.494-.333.989-.767.989-1.567 0-1.105-.895-2-2-2s-2 .895-2 2M11.999 16c-.138 0-.25.112-.249.25 0 .138.112.25.25.25s.25-.112.25-.25-.112-.25-.251-.25" />
</g>
</svg>
</div>
</div>
<div class="nav-lower-list">
<div class="relative link" sfngTipUpTrigger="navTools" sfngTipUpPassive tooltip="Version and Tools"
sfngTooltipDelay="1000" snfgTooltipPosition="right" (click)="settingsMenu.dropdown.toggle(settingsMenuTrigger)"
cdkOverlayOrigin #settingsMenuTrigger="cdkOverlayOrigin" [class.active]="settingsMenu.dropdown.isOpen">
<span *ngIf="versions?.Channel !== 'stable'"
class="absolute w-1.5 h-1.5 bg-yellow-300 rounded-full top-1.5 right-1.5"></span>
<svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="help"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke-linecap="round" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linejoin="round">
<path d="M11.835,15l5,5c0.828,0.828 2.172,0.828 3,0v0c0.828,-0.828 0.828,-2.172 0,-3l-5,-5"></path>
<path
d="M20.916,5.847c0.024,0.023 0.042,0.053 0.051,0.085c0.47,1.567 0.106,3.33 -1.132,4.568c-1.251,1.251 -3.038,1.609 -4.617,1.117l-8.347,8.347c-0.813,0.813 -2.139,0.874 -2.98,0.09c-0.884,-0.823 -0.902,-2.207 -0.056,-3.054l8.383,-8.383c-0.492,-1.579 -0.134,-3.366 1.117,-4.617c1.238,-1.238 3.001,-1.602 4.568,-1.132c0.032,0.01 0.062,0.027 0.085,0.051l0.162,0.162c0.078,0.078 0.078,0.205 0,0.283l-2.635,2.636l2.32,2.32l2.636,-2.636c0.078,-0.078 0.205,-0.078 0.283,0l0.162,0.163Z">
</path>
<path
d="M2.933,4.293l0.674,2.023c0.136,0.409 0.518,0.684 0.949,0.684h2.279v-2.279c0,-0.43 -0.275,-0.813 -0.684,-0.949l-2.023,-0.674c-0.18,-0.06 -0.378,-0.013 -0.512,0.121l-0.562,0.562c-0.134,0.134 -0.181,0.332 -0.121,0.512Z">
</path>
<path d="M6.84,7l3.5,3.5"></path>
</g>
</svg>
</div>
<app-menu #settingsMenu offsetY="0" offsetX="10" overlayClass="rounded-t">
<div class="flex flex-col p-4 text-xxs">
<span class="text-secondary">
Version: <span class="text-primary">{{ versions?.Core?.Version }} </span>
</span>
<span class="text-secondary">
Release Channel:
<span class="uppercase text-primary"
[class.text-yellow-300]="versions?.Channel !== 'stable'">{{ versions?.Channel }}</span>
</span>
</div>
<app-menu-item (click)="downloadUpdates($event)">Check for Updates</app-menu-item>
<app-menu-item (click)="openChangeLog()">View Changelog</app-menu-item>
<app-menu-item (click)="reloadUI($event)">Reload UI</app-menu-item>
<app-menu-item *appExpertiseLevel="'developer'" (click)="showIntro()">
Show Intro Screen
</app-menu-item>
<app-menu-item (click)="reinitSPN($event)">Re-Initialize SPN</app-menu-item>
<app-menu-item (click)="logoutCompletely($event)">Logout Completely</app-menu-item>
<app-menu-item (click)="resetBroadcastState()">Reset Broadcast State</app-menu-item>
<app-menu-item (click)="clearDNSCache($event)">Clear DNS Cache</app-menu-item>
<app-menu-item (click)="openDataDir($event)">Open Data Directory</app-menu-item>
<app-menu-item (click)="copyDebugInfo($event)">Copy Debug Info</app-menu-item>
<app-menu-item (click)="cleanupHistory($event)">Cleanup Network History</app-menu-item>
</app-menu>
<!-- Power Menu -->
<div sfngTipUpTrigger="navPower" sfngTipUpPassive tooltip="Shutdown and Restart" sfngTooltipDelay="1000"
snfgTooltipPosition="right" class="link" (click)="powerMenu.dropdown.toggle(powerMenuTrigger)" cdkOverlayOrigin
#powerMenuTrigger="cdkOverlayOrigin" [class.active]="powerMenu.dropdown.isOpen">
<svg version="1.1" viewBox="0 0 24 24" class="help" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g fill="none">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M18.364,5.63604c3.51472,3.51472 3.51472,9.2132 0,12.7279c-3.51472,3.51472 -9.2132,3.51472 -12.7279,0c-3.51472,-3.51472 -3.51472,-9.2132 -1.77636e-15,-12.7279c3.51472,-3.51472 9.2132,-3.51472 12.7279,-1.77636e-15">
</path>
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12,7v5">
</path>
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M15.534,8.466c1.952,1.952 1.952,5.117 0,7.069c-1.952,1.952 -5.117,1.952 -7.069,0c-1.952,-1.952 -1.952,-5.117 0,-7.069">
</path>
</g>
</svg>
</div>
<app-menu #powerMenu offsetY="0" offsetX="10" overlayClass="rounded-t">
<app-menu-item (click)="shutdown($event)">Shutdown</app-menu-item>
<app-menu-item (click)="restart($event)">Restart</app-menu-item>
</app-menu>
</div>

View File

@@ -0,0 +1,98 @@
:host {
height: 100vh;
top: 0px;
position: sticky;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
user-select: none;
.logo-image {
@apply w-6 -top-3 -left-3 absolute;
position: absolute;
}
svg {
&:not(.connected) {
animation-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);
path.inner {
fill: theme('colors.info.red');
}
}
}
div.nav-list {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
div.nav-lower-list {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding-bottom: 1.5rem;
}
div.link {
@apply my-2;
width: 2rem;
height: 2rem;
border-radius: 10px;
display: flex;
justify-content: space-around;
align-items: center;
cursor: pointer;
& {
outline: none;
svg,
fa-icon {
opacity: .5;
}
}
&:target,
&.active {
background-color: #2c2c2c;
svg,
fa-icon {
opacity: 1;
transform: scale(1.08);
}
}
&:hover {
svg,
fa-icon {
opacity: 1;
}
}
svg,
fa-icon {
&.dash,
&.spn,
&.monitor,
&.app,
&.help,
&.settings {
@apply text-white;
width: 1.1rem;
position: relative;
stroke: currentColor;
}
}
}
}

View File

@@ -0,0 +1,298 @@
import { INTEGRATION_SERVICE, IntegrationService } from 'src/app/integration';
import { ConnectedPosition } from '@angular/cdk/overlay';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, OnInit, Output, inject } from '@angular/core';
import { ConfigService, DebugAPI, PortapiService, SPNService, StringSetting } from '@safing/portmaster-api';
import { tap } from 'rxjs/operators';
import { AppComponent } from 'src/app/app.component';
import { NotificationType, NotificationsService, StatusService, VersionStatus } from 'src/app/services';
import { ActionIndicatorService } from 'src/app/shared/action-indicator';
import { fadeInAnimation, fadeOutAnimation } from 'src/app/shared/animations';
import { ExitService } from 'src/app/shared/exit-screen';
import { TauriIntegrationService } from 'src/app/integration/taur-app';
@Component({
selector: 'app-navigation',
templateUrl: './navigation.html',
styleUrls: ['./navigation.scss'],
exportAs: 'navigation',
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
fadeInAnimation,
fadeOutAnimation,
]
})
export class NavigationComponent implements OnInit {
private readonly integration = inject(INTEGRATION_SERVICE);
/** Emits the current portapi connection state on changes. */
readonly connected$ = this.portapi.connected$;
/** @private The available and selected resource versions. */
versions: VersionStatus | null = null;
/** Whether or not we have new, unseen notifications */
hasNewNotifications = false;
/** The color to use for the notifcation-available hint (dot) */
notificationColor: string = 'text-green-300';
/** Whether or not we have new, unseen prompts */
hasNewPrompts = false;
/** Whether or not prompting is globally enabled. */
globalPromptingEnabled = false;
@Output()
sideDashChange = new EventEmitter<'collapsed' | 'expanded' | 'force-overlay'>();
/** Whether or not the side dash should be expanded or collapsed */
sideDashStatus: 'collapsed' | 'expanded' = 'expanded';
constructor(
private portapi: PortapiService,
private exitService: ExitService,
private statusService: StatusService,
private configService: ConfigService,
private appComponent: AppComponent,
private debugAPI: DebugAPI,
private actionIndicator: ActionIndicatorService,
private notificationService: NotificationsService,
private spnService: SPNService,
private cdr: ChangeDetectorRef
) { }
dropDownPositions: ConnectedPosition[] = [
{
originX: 'end',
originY: 'top',
overlayX: 'start',
overlayY: 'top'
}
]
ngOnInit() {
const mql = window.matchMedia('(max-width: 1200px)');
if (mql.matches) {
this.sideDashStatus = 'collapsed';
this.sideDashChange.next(this.sideDashStatus);
}
mql.addEventListener('change', () => {
if (mql.matches) {
this.sideDashStatus = 'collapsed';
} else {
this.sideDashStatus = 'expanded';
}
this.sideDashChange.next(this.sideDashStatus);
})
this.statusService.getVersions()
.subscribe(versions => {
this.versions = versions;
this.cdr.markForCheck();
});
this.configService.watch<StringSetting>('filter/defaultAction')
.subscribe(defaultAction => {
this.globalPromptingEnabled = defaultAction === 'ask';
this.cdr.markForCheck();
})
this.notificationService.new$
.subscribe(notif => {
if (notif.some(n => n.Type === NotificationType.Prompt && n.EventID.startsWith("filter:prompt"))) {
this.hasNewPrompts = true;
if (this.integration instanceof TauriIntegrationService) {
this.integration.openPrompt();
}
} else {
this.hasNewPrompts = false;
if (this.integration instanceof TauriIntegrationService) {
this.integration.closePrompt();
}
}
if (notif.some(n => !n.EventID.startsWith("filter:prompt"))) {
this.hasNewNotifications = true;
} else {
this.hasNewNotifications = false;
}
if (notif.some(n => n.Type === NotificationType.Error)) {
this.notificationColor = 'text-red-300';
} else if (notif.some(n => n.Type === NotificationType.Warning)) {
this.notificationColor = 'text-yellow-300';
} else {
this.notificationColor = 'text-green-300';
}
this.cdr.markForCheck();
})
}
toggleSideDash(event: MouseEvent) {
let notify: 'expanded' | 'collapsed' | 'force-overlay' = this.sideDashStatus;
if (this.sideDashStatus === 'collapsed') {
this.sideDashStatus = 'expanded';
notify = 'expanded';
if (event.shiftKey) {
notify = 'force-overlay'
}
} else {
this.sideDashStatus = 'collapsed';
notify = 'collapsed'
}
this.sideDashChange.next(notify);
}
/**
* @private
* Injects a ui/reload event and performs a complete
* reload of the window once the portmaster re-opened the
* UI bundle.
*/
reloadUI(_: Event) {
this.portapi.reloadUI()
.pipe(
tap(() => {
setTimeout(() => window.location.reload(), 1000)
})
)
.subscribe(this.actionIndicator.httpObserver(
'Reloading UI ...',
'Failed to Reload UI',
))
}
/** Re-initialize the SPN */
reinitSPN(_: Event) {
this.portapi.reinitSPN()
.subscribe(this.actionIndicator.httpObserver(
'Re-initialized SPN',
'Failed to re-initialize the SPN'
))
}
/** Logs the user out of the SPN completely by purgin the user profile from the local storage */
logoutCompletely(_: Event) {
this.spnService.logout(true)
.subscribe(this.actionIndicator.httpObserver(
'Logout',
'You have been logged out of the SPN completely.'
))
}
/**
* @private
* Clear the DNS name cache.
*/
clearDNSCache(_: Event) {
this.portapi.clearDNSCache()
.subscribe(this.actionIndicator.httpObserver(
'DNS Cache Cleared',
'Failed to Clear DNS Cache.',
))
}
cleanupHistory(_: Event) {
this.portapi.cleanupHistory()
.subscribe(this.actionIndicator.httpObserver(
'Network History Cleaned Up',
'Failed to Cleanup Network History.'
))
}
/**
* @private
* Trigger downloading of updates
*
* @param event - The mouse event
*/
downloadUpdates(event: Event) {
this.portapi.checkForUpdates()
.subscribe(this.actionIndicator.httpObserver(
'Downloading Updates ...',
'Failed to Check for Updates',
))
}
/**
* @private
* Trigger a shutdown of the portmaster-core service
*/
shutdown(_: Event) {
this.exitService.shutdownPortmaster();
}
/**
* @private
* Trigger a restart of the portmaster-core service. Requires
* that portmaster has been started with a service-wrapper.
*
* @param event The mouse event
*/
restart(event: Event) {
// prevent default and stop-propagation to avoid
// expanding the accordion body.
event.preventDefault();
event.stopPropagation();
this.portapi.restartPortmaster()
.subscribe(this.actionIndicator.httpObserver(
'Restarting ...',
'Failed to Restart',
))
}
/**
* @private
* Opens the data-directory of the portmaster installation.
* Requires the application to run inside electron.
*/
async openDataDir(event: Event) {
const dir = await this.integration.getInstallDir()
await this.integration.openExternal(dir);
}
openChangeLog() {
const url = "https://github.com/safing/portmaster/releases";
this.integration.openExternal(url);
}
showIntro() {
this.appComponent.showIntro()
}
resetBroadcastState() {
this.portapi.resetBroadcastState()
.subscribe(this.actionIndicator.httpObserver(
'Broadcast State Cleared',
'Failed to Reset Broadcast State.',
))
}
copyDebugInfo(event: Event) {
// prevent default and stop-propagation to avoid
// expanding the accordion body.
event.preventDefault();
event.stopPropagation();
this.debugAPI.getCoreDebugInfo()
.subscribe(
async info => {
await this.integration.writeToClipboard(info);
},
err => {
console.error(err);
this.actionIndicator.error('Failed loading debug data', err);
}
)
}
}

View File

@@ -0,0 +1,10 @@
<div sfngTipUpTrigger="navShield" sfngTipUpPassive class="relative flex flex-row w-full gap-2 px-2 pb-4 justify-evenly">
<app-status-pilot class="block w-32"></app-status-pilot>
</div>
<app-feature-scout></app-feature-scout>
<app-notification-list></app-notification-list>
<app-spn-login *ngIf="spnLoginRequired"></app-spn-login>
<app-network-scout *ngIf="!spnLoginRequired" class="flex-grow overflow-auto"></app-network-scout>

View File

@@ -0,0 +1,11 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
overflow: hidden;
overflow-y: hidden;
width: 419px;
@apply pt-4;
}

View File

@@ -0,0 +1,13 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-side-dash',
templateUrl: './side-dash.html',
styleUrls: ['./side-dash.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SideDashComponent {
/** Whether or not a SPN account login is required */
spnLoginRequired = false;
}