cosmetics plus a slew of little frontend rendering bugs

This commit is contained in:
Matt Hill
2021-08-13 16:16:24 -06:00
committed by Aiden McClelland
parent c18a119c70
commit 7dc53a4e85
44 changed files with 518 additions and 618 deletions

View File

@@ -1,7 +1,7 @@
<ion-item button>
<ion-icon slot="start" [name]="action.icon"></ion-icon>
<ion-label class="ion-text-wrap">
<ion-icon slot="start" [name]="action.icon" size="large"></ion-icon>
<ion-label>
<h1>{{ action.name }}</h1>
<h2>{{ action.description }}</h2>
<p>{{ action.description }}</p>
</ion-label>
</ion-item>

View File

@@ -110,15 +110,13 @@ export class AppActionsPage {
pkgId: this.pkgId,
},
component: AppRestoreComponent,
backdropDismiss: false,
})
modal.onWillDismiss().then(res => {
const data = res.data
if (data.error) this.errToast.present(data.error)
if (res.role === 'success') this.navCtrl.back()
})
return await modal.present()
await modal.present()
}
async uninstall (manifest: Manifest) {

View File

@@ -1,14 +1,14 @@
<ion-item>
<ion-icon slot="start" [name]="interface.def.ui ? 'desktop-outline' : 'terminal-outline'"></ion-icon>
<ion-label class="ion-text-wrap">
<ion-icon slot="start" size="large" [name]="interface.def.ui ? 'desktop-outline' : 'terminal-outline'"></ion-icon>
<ion-label>
<h1>{{ interface.def.name }}</h1>
<h2>{{ interface.def.description }}</h2>
</ion-label>
</ion-item>
<div style="padding-left: 54px;">
<div style="padding-left: 64px;">
<!-- has tor -->
<ion-item *ngIf="interface.addresses['tor-address'] as tor">
<ion-label class="ion-text-wrap">
<ion-label>
<h2>Tor Address</h2>
<p>{{ tor }}</p>
</ion-label>
@@ -23,7 +23,7 @@
</ion-item>
<!-- no tor -->
<ion-item *ngIf="!interface.addresses['tor-address']">
<ion-label class="ion-text-wrap">
<ion-label>
<h2>Tor Address</h2>
<p>Service does not use a Tor Address</p>
</ion-label>
@@ -31,7 +31,7 @@
<!-- lan -->
<ion-item *ngIf="interface.addresses['lan-address'] as lan">
<ion-label class="ion-text-wrap">
<ion-label>
<h2>LAN Address</h2>
<p>{{ lan }}</p>
</ion-label>
@@ -46,7 +46,7 @@
</ion-item>
<!-- no lan -->
<ion-item *ngIf="!interface.addresses['lan-address']">
<ion-label class="ion-text-wrap">
<ion-label>
<h2>LAN Address</h2>
<p>Service does not use a LAN Address</p>
</ion-label>

View File

@@ -1,4 +0,0 @@
.vertical-align {
display: inline-block;
vertical-align: middle;
}

View File

@@ -24,8 +24,8 @@
<ion-row>
<ion-col *ngFor="let pkg of pkgs | keyvalue : asIsOrder" sizeXs="4" sizeSm="3" sizeLg="3" sizeXl="2">
<ion-card class="installed-card" [routerLink]="['/services', pkg.value.entry.manifest.id]">
<div class="launch-container" *ngIf="pkg.value.entry | hasUi">
<div class="launch-button-triangle" (click)="launchUi(pkg.value.entry, $event)" [class.launch-disabled]="!(pkg.value.entry | isLaunchable)">
<div class="launch-container" *ngIf="pkg.value.entry.manifest.interfaces | hasUi">
<div class="launch-button-triangle" (click)="launchUi(pkg.value.entry, $event)" [class.launch-disabled]="!(pkg.value.entry.state | isLaunchable : pkg.value.entry.installed.status.main.status : pkg.value.entry.manifest.interfaces)">
<ion-icon name="open-outline"></ion-icon>
</div>
</div>

View File

@@ -6,7 +6,8 @@
<ion-title>Properties</ion-title>
<ion-buttons slot="end">
<ion-button (click)="refresh()">
<ion-icon slot="icon-only" name="refresh-outline"></ion-icon>
<ion-icon slot="start" name="refresh-outline"></ion-icon>
Refresh
</ion-button>
</ion-buttons>
</ion-toolbar>
@@ -18,14 +19,14 @@
<ng-template #loaded>
<!-- not running -->
<ion-item *ngIf="!running" class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<ion-label>
<p><ion-text color="warning">Service not running. Information on this page could be inaccurate.</ion-text></p>
</ion-label>
</ion-item>
<!-- no properties -->
<ion-item *ngIf="properties | empty">
<ion-label class="ion-text-wrap">
<ion-label>
<p>No properties.</p>
</ion-label>
</ion-item>
@@ -38,7 +39,7 @@
<ion-button *ngIf="prop.value.description" fill="clear" slot="start" (click)="presentDescription(prop, $event)">
<ion-icon slot="icon-only" name="help-circle-outline"></ion-icon>
</ion-button>
<ion-label class="ion-text-wrap">
<ion-label>
<h2>{{ prop.key }}</h2>
</ion-label>
</ion-item>
@@ -47,7 +48,7 @@
<ion-button *ngIf="prop.value.description" fill="clear" slot="start" (click)="presentDescription(prop, $event)">
<ion-icon slot="icon-only" name="help-circle-outline"></ion-icon>
</ion-button>
<ion-label class="ion-text-wrap">
<ion-label>
<h2>{{ prop.key }}</h2>
<p>{{ prop.value.masked && !unmasked[prop.key] ? (prop.value.value | mask ) : (prop.value.value | truncateEnd : 100) }}</p>
</ion-label>

View File

@@ -18,35 +18,35 @@
</ion-header>
<ion-content>
<ng-container *ngIf="pkg">
<ion-item-group>
<!-- ** always ** -->
<ion-item-divider>Status</ion-item-divider>
<ion-item>
<ion-label style="overflow: visible;">
<status size="x-large" weight="500" [rendering]="rendering"></status>
</ion-label>
<ion-button slot="end" class="action-button" *ngIf="pkg.state === PackageState.Installed && (pkg | hasUi)" [disabled]="!(pkg | isLaunchable)" (click)="launchUiTab()">
<ion-icon slot="start" name="open-outline"></ion-icon>
Open UI
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.NeedsConfig" [routerLink]="['config']">
Configure
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : rendering.feStatus" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.DependencyIssue" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.Stopped" color="success" (click)="tryStart()">
Start
</ion-button>
</ion-item>
<!-- ** iff installed ** -->
<ng-container *ngIf="pkg.state === PackageState.Installed">
<ion-item-group>
<!-- ** always ** -->
<ion-item-divider>Status</ion-item-divider>
<ion-item>
<ion-label style="overflow: visible;">
<status size="x-large" weight="500" [rendering]="rendering"></status>
</ion-label>
<ion-button slot="end" class="action-button" *ngIf="pkg.state === PackageState.Installed && (pkg.manifest.interfaces | hasUi)" [disabled]="!(pkg.state | isLaunchable : pkg.installed.status.main.status : pkg.manifest.interfaces)" (click)="launchUiTab()">
<ion-icon slot="start" name="open-outline"></ion-icon>
Open UI
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.NeedsConfig" [routerLink]="['config']">
Configure
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : rendering.feStatus" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.DependencyIssue" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button slot="end" class="action-button" *ngIf="rendering.feStatus === FeStatus.Stopped" color="success" (click)="tryStart()">
Start
</ion-button>
</ion-item>
<!-- ** iff && !restoring/backing-up ** -->
<ng-container *ngIf="pkg.state === PackageState.Installed">
<!-- ** iff !restoring/backing-up ** -->
<ng-container *ngIf="!([PackageMainStatus.BackingUp, PackageMainStatus.Restoring] | includes : mainStatus.status); else maintenance">
<!-- ** iff health checks ** -->
<ng-container *ngIf="!(mainStatus.health | empty)">
<ion-item-divider>Health Checks</ion-item-divider>
@@ -78,7 +78,7 @@
<ion-thumbnail slot="start">
<img [src]="pkg.installed['dependency-info'][dep.key].icon" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<ion-label>
<h2 style="font-family: 'Montserrat'">{{ pkg.installed['dependency-info'][dep.key].manifest.title }}</h2>
<p>{{ pkg.manifest.dependencies[dep.key].version | displayEmver }}</p>
<p><ion-text [color]="pkg.installed.status['dependency-errors'][dep.key] ? 'warning' : 'success'">{{ pkg.installed.status['dependency-errors'][dep.key] ? pkg.installed.status['dependency-errors'][dep.key].type : 'satisfied' }}</ion-text></p>
@@ -112,27 +112,30 @@
</ion-item>
</ng-container>
</ng-container>
</ion-item-group>
<ng-template #maintenance>
App is undergoing maintenance.
</ng-template>
</ng-container>
</ion-item-group>
<!-- ** installed or updating ** -->
<div *ngIf="[PackageState.Installing, PackageState.Updating] | includes : pkg.state" style="padding: 16px;">
<p>Downloading: {{ (pkg['install-progress'] | installState).downloadProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['download-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).downloadProgress / 100"
></ion-progress-bar>
<!-- ** installing or updating ** -->
<div *ngIf="[PackageState.Installing, PackageState.Updating] | includes : pkg.state" style="padding: 16px;">
<p>Downloading: {{ (pkg['install-progress'] | installState).downloadProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['download-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).downloadProgress / 100"
></ion-progress-bar>
<p>Validating: {{ (pkg['install-progress'] | installState).validateProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['validation-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).validateProgress / 100"
></ion-progress-bar>
<p>Validating: {{ (pkg['install-progress'] | installState).validateProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['validation-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).validateProgress / 100"
></ion-progress-bar>
<p>Installing: {{ (pkg['install-progress'] | installState).unpackProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['unpack-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).unpackProgress / 100"
></ion-progress-bar>
</div>
</ng-container>
<p>Installing: {{ (pkg['install-progress'] | installState).unpackProgress }}%</p>
<ion-progress-bar
[color]="pkg['install-progress']['unpack-complete'] ? 'success' : 'secondary'"
[value]="(pkg['install-progress'] | installState).unpackProgress / 100"
></ion-progress-bar>
</div>
</ion-content>

View File

@@ -8,7 +8,7 @@ import { wizardModal } from 'src/app/components/install-wizard/install-wizard.co
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { ConfigService } from 'src/app/services/config.service'
import { PatchDbService } from 'src/app/services/patch-db/patch-db.service'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, MainStatus, PackageDataEntry, PackageState } from 'src/app/services/patch-db/data-model'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, MainStatus, PackageDataEntry, PackageMainStatus, PackageState } from 'src/app/services/patch-db/data-model'
import { FEStatus, PkgStatusRendering, renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
import { ConnectionService } from 'src/app/services/connection.service'
import { ErrorToastService } from 'src/app/services/error-toast.service'
@@ -31,6 +31,7 @@ export class AppShowPage {
rendering: PkgStatusRendering
Math = Math
mainStatus: MainStatus
PackageMainStatus = PackageMainStatus
@ViewChild(IonContent) content: IonContent
subs: Subscription[] = []

View File

@@ -1,6 +1,5 @@
<ion-header>
<ion-toolbar *ngIf="!pageLoading">
<ion-searchbar color="dark" (ionChange)="search($event)" debounce="400"></ion-searchbar>
<ion-toolbar>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
@@ -8,11 +7,23 @@
</ion-header>
<ion-content class="ion-padding">
<text-spinner *ngIf="pageLoading; else pageLoaded" text="Loading Marketplace"></text-spinner>
<h1 style="font-family: 'Montserrat'; font-weight: 100px; margin: 32px 0;" class="ion-text-center">Embassy Marketplace</h1>
<ion-searchbar color="dark" (ionChange)="search($event)" debounce="400" style="padding-bottom: 32px;"></ion-searchbar>
<!-- page loading -->
<ng-container *ngIf="pageLoading; else pageLoaded">
<div class="scrollable ion-text-center">
<ion-button *ngFor="let cat of ['', '', '', '', '', '', '']" fill="clear">
<ion-skeleton-text animated style="width: 80px; border-radius: 0;"></ion-skeleton-text>
</ion-button>
</div>
<div class="divider" style="margin: 24px 0;"></div>
</ng-container>
<!-- page loaded -->
<ng-template #pageLoaded>
<h1 style="font-family: 'Montserrat'; font-weight: 100px; margin: 0 0 32px 0;" class="ion-text-center">Embassy Marketplace</h1>
<div class="scrollable ion-text-center">
<ion-button
*ngFor="let cat of data.categories"
@@ -25,57 +36,75 @@
</div>
<div class="divider" style="margin: 24px;"></div>
<div *ngIf="pkgsLoading; else loaded" style="margin-top: 64px;" class="ion-text-center">
<text-spinner text="Loading Packages"></text-spinner>
</div>
<ng-template #loaded>
<ion-grid>
<ion-row>
<ion-col *ngIf="eos && category === 'featured'" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item button class="eos-item" (click)="updateEos()">
<ion-thumbnail slot="start">
<img src="assets/img/icon.png" />
</ion-thumbnail>
<ion-label>
<h3>Now Available...</h3>
<h2>Embassy OS {{ eos.version }}</h2>
<p>{{ eos.headline }}</p>
</ion-label>
</ion-item>
</ion-col>
<ion-col *ngFor="let pkg of pkgs" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item [routerLink]="['/marketplace', pkg.manifest.id]">
<ion-thumbnail slot="start">
<img [src]="pkg.icon" />
</ion-thumbnail>
<ion-label>
<h2 style="font-family: 'Montserrat'; font-weight: bold;">{{ pkg.manifest.title }}</h2>
<h3>{{ pkg.manifest.description.short }}</h3>
<ng-container *ngIf="localPkgs[pkg.manifest.id] as localPkg; else none">
<p *ngIf="localPkg.state === PackageState.Installed">
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0" color="success">Installed</ion-text>
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1" color="warning">Update Available</ion-text>
</p>
<p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state">
<ion-text color="primary">{{ localPkg.state | titlecase }}...{{ (localPkg['install-progress'] | installState).totalProgress }}%</ion-text>
</p>
<p *ngIf="localPkg.state === PackageState.Removing">
<ion-text color="warning">{{ localPkg.state | Removing }}</ion-text>
<ion-spinner name="dots" color="warning"></ion-spinner>
</p>
</ng-container>
<ng-template #none>
<p>Not Installed</p>
</ng-template>
</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-template>
</ng-template>
<!-- packages loading -->
<ng-container *ngIf="pkgsLoading; else pkgsLoaded">
<ion-grid>
<ion-row>
<ion-col *ngFor="let pkg of ['', '', '', '']" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item>
<ion-thumbnail slot="start">
<ion-skeleton-text style="border-radius: 100%;" animated></ion-skeleton-text>
</ion-thumbnail>
<ion-label>
<ion-skeleton-text animated style="width: 150px; height: 18px; margin-bottom: 8px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 400px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 400px;"></ion-skeleton-text>
</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
<!-- packages loaded -->
<ng-template #pkgsLoaded>
<ion-grid>
<ion-row>
<ion-col *ngIf="eos && category === 'featured'" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item button class="eos-item" (click)="updateEos()">
<ion-thumbnail slot="start">
<img src="assets/img/icon.png" />
</ion-thumbnail>
<ion-label>
<h3>Now Available...</h3>
<h2>Embassy OS {{ eos.version }}</h2>
<p>{{ eos.headline }}</p>
</ion-label>
</ion-item>
</ion-col>
<ion-col *ngFor="let pkg of pkgs" sizeXs="12" sizeSm="12" sizeMd="6">
<ion-item [routerLink]="['/marketplace', pkg.manifest.id]">
<ion-thumbnail slot="start">
<img [src]="pkg.icon" />
</ion-thumbnail>
<ion-label>
<h2 style="font-family: 'Montserrat'; font-weight: bold;">{{ pkg.manifest.title }}</h2>
<h3>{{ pkg.manifest.description.short }}</h3>
<ng-container *ngIf="localPkgs[pkg.manifest.id] as localPkg; else none">
<p *ngIf="localPkg.state === PackageState.Installed">
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 0" color="success">Installed</ion-text>
<ion-text *ngIf="(pkg.manifest.version | compareEmver : localPkg.manifest.version) === 1" color="warning">Update Available</ion-text>
</p>
<p *ngIf="[PackageState.Installing, PackageState.Updating] | includes : localPkg.state">
<ion-text color="primary">{{ localPkg.state | titlecase }}...{{ (localPkg['install-progress'] | installState).totalProgress }}%</ion-text>
</p>
<p *ngIf="localPkg.state === PackageState.Removing">
<ion-text color="warning">{{ localPkg.state | Removing }}</ion-text>
<ion-spinner name="dots" color="warning"></ion-spinner>
</p>
</ng-container>
<ng-template #none>
<p>Not Installed</p>
</ng-template>
</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ng-template>
<ion-infinite-scroll [disabled]="!needInfinite" (ionInfinite)="doInfinite($event)">
<ion-infinite-scroll-content loadingSpinner="lines"></ion-infinite-scroll-content>
</ion-infinite-scroll>

View File

@@ -70,7 +70,7 @@
<!-- recommendation -->
<ion-item *ngIf="rec && showRec" class="rec-item">
<ion-label class="ion-text-wrap">
<ion-label>
<h2 style="display: flex; align-items: center;">
<ion-thumbnail style="height: 3vh; width: 3vh; margin: 5px" slot="start">
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
@@ -98,14 +98,14 @@
</ion-button>
</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap" >
<ion-label>
<div id='release-notes' [innerHTML]="pkg.manifest['release-notes'] | markdown"></div>
</ion-label>
</ion-item>
<!-- description -->
<ion-item-divider>Description</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap">
<ion-label>
<div id="release-notes" class="release-notes">{{ pkg.manifest.description.long }}</div>
</ion-label>
</ion-item>
@@ -119,7 +119,7 @@
<ion-thumbnail slot="start">
<img [src]="pkg['dependency-metadata'][dep.key].icon" />
</ion-thumbnail>
<ion-label class="ion-text-wrap">
<ion-label>
<h2>
{{ pkg['dependency-metadata'][dep.key].title }}
<span *ngIf="dep.value.recommended"> (recommended)</span>

View File

@@ -11,14 +11,30 @@
</ion-header>
<ion-content>
<ion-refresher slot="fixed" (ionRefresh)="doRefresh($event)">
<ion-refresher-content pullingIcon="lines" refreshingSpinner="lines"></ion-refresher-content>
</ion-refresher>
<text-spinner *ngIf="loading" text="Loading Notifications"></text-spinner>
<!-- loading -->
<ion-item-group *ngIf="loading">
<ion-item-divider>
<ion-button slot="end" fill="clear">
<ion-skeleton-text style="width: 90px; height: 14px; border-radius: 0;" animated></ion-skeleton-text>
</ion-button>
</ion-item-divider>
<ion-item *ngFor="let entry of ['', '', '', '']">
<ion-label>
<ion-skeleton-text animated style="width: 15%; height: 20px; margin-bottom: 12px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 50%; margin-bottom: 18px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 20%;"></ion-skeleton-text>
</ion-label>
<ion-button slot="end" fill="clear">
<ion-skeleton-text animated style="width: 20px; height: 20px; border-radius: 0"></ion-skeleton-text>
</ion-button>
</ion-item>
</ion-item-group>
<!-- no notifications -->
<!-- not loading -->
<ng-container *ngIf="!loading">
<!-- no notifications -->
<ion-item-group *ngIf="!notifications.length">
<div
style="
@@ -28,8 +44,8 @@
left: 50%;
transform: translate(-50%, -50%);"
>
<ion-icon style="font-size: 84px; color: #2c3038" name="mail-outline"></ion-icon>
<h4 style="color: #2c3038; margin-top: 0px">Inbox Empty</h4>
<ion-icon style="font-size: 84px; color: #595959" name="mail-outline"></ion-icon>
<h4 style="color: #595959; margin-top: 0px">Inbox Empty</h4>
</div>
</ion-item-group>
@@ -42,7 +58,7 @@
</ion-button>
</ion-item-divider>
<ion-item *ngFor="let not of notifications; let i = index">
<ion-label class="ion-text-wrap">
<ion-label>
<h2>
<ion-text [color]="not | notificationColor"><b>{{ not.title }}</b></ion-text>
</h2>

View File

@@ -1,3 +1,3 @@
.notification-message {
margin: 10px 0 12px 0;
margin: 6px 0 8px 0;
}

View File

@@ -32,7 +32,7 @@ export class NotificationsPage {
this.loading = false
}
async doRefresh (e: any) {
async refresh (e: any) {
this.page = 1
this.notifications = await this.getNotifications(),
e.target.complete()

View File

@@ -11,25 +11,27 @@
<ion-item-group>
<!-- about -->
<ion-item>
<ion-label class="ion-text-wrap">
<p class="ion-padding-bottom">About</p>
<h2>You can connect to your Embassy over your Local Area Network (LAN). This can be useful for achieving a faster experience, as well as a fallback in case the Tor network is experiencing issues.</h2>
<ion-item class="ion-padding-bottom">
<ion-label>
<h2>
Connecting to your Embassy over the Local Area Network (LAN) is great for achieving a faster experience, as well as a fallback in case Tor is experiencing issues.
<a [href]="docsUrl" target="_blank">View instructions</a>
</h2>
</ion-label>
</ion-item>
<ion-item [href]="docsUrl" target="_blank" detail="false">
<ion-icon slot="start" name="list-outline"></ion-icon>
<ion-label>View Instructions</ion-label>
</ion-item>
<ion-item button (click)="installCert()" [disabled]="lanDisabled">
<ion-icon slot="start" name="download-outline"></ion-icon>
<ion-label>Download Root Certificate Authority</ion-label>
<ion-icon slot="start" name="download-outline" size="large"></ion-icon>
<ion-label>
<h1>Download Root CA</h1>
<p>Download and trust your Embassy's Root Certificate Authority to achieve a secure connection on the LAN.</p>
</ion-label>
</ion-item>
<ng-container *ngIf="lanDisabled">
<ion-item-divider></ion-item-divider>
<ion-item>
<ion-label class="ion-text-wrap">
<ion-label>
<p class="ion-padding-bottom">Setup</p>
<ion-text color="warning" [innerHtml]="lanDisabledExplanation[lanDisabled]"></ion-text>
</ion-label>
@@ -37,16 +39,12 @@
</ng-container>
<!-- Refresh Network -->
<ion-item-divider></ion-item-divider>
<ion-item>
<ion-label class="ion-text-wrap">
<p style="padding-bottom: 6px;">Troubleshooting</p>
<h2>If you are having issues connecting to your Embassy over LAN, try refreshing your LAN services by clicking the button below.</h2>
</ion-label>
</ion-item>
<ion-item button (click)="refreshLAN()" detail="false">
<ion-icon slot="start" name="refresh-outline"></ion-icon>
<ion-label>Refresh LAN</ion-label>
<ion-icon slot="start" name="refresh-outline" size="large"></ion-icon>
<ion-label>
<h1>Refresh LAN</h1>
<p>If you are having issues connecting to your Embassy over LAN, try refreshing your LAN services by clicking the button below.</p>
</ion-label>
</ion-item>
</ion-item-group>

View File

@@ -8,14 +8,37 @@
</ion-header>
<ion-content class="ion-padding-top">
<text-spinner *ngIf="loading" text="Loading Sessions"></text-spinner>
<!-- loading -->
<ion-item-group *ngIf="loading">
<div *ngFor="let entry of ['first', 'second']">
<ion-item-divider>
<ion-skeleton-text animated style="width: 120px; height: 20px;"></ion-skeleton-text>
</ion-item-divider>
<ion-item style="padding-bottom: 6px;">
<ion-avatar slot="start" style="margin-right: 30px;">
<ion-skeleton-text animated style="width: 40px; height: 40px; border-radius: 0;"></ion-skeleton-text>
</ion-avatar>
<ion-label>
<ion-skeleton-text animated style="width: 150px; height: 20px; margin-bottom: 10px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 250px; height: 14px; margin-bottom: 12px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 350px;"></ion-skeleton-text>
</ion-label>
<ion-button *ngIf="entry === 'second'" slot="end" fill="clear">
<ion-skeleton-text animated style="width: 60px; border-radius: 0"></ion-skeleton-text>
</ion-button>
</ion-item>
</div>
</ion-item-group>
<!-- not loading -->
<ion-item-group *ngIf="!loading">
<ion-item-divider>Current Session</ion-item-divider>
<ion-item-divider>This Session</ion-item-divider>
<ion-item *ngIf="sessionInfo.sessions[sessionInfo.current] as current">
<ion-icon slot="start" [name]="getPlatformIcon(current.metadata.platforms)"></ion-icon>
<ion-label class="ion-text-wrap">
<ion-icon slot="start" size="large" [name]="getPlatformIcon(current.metadata.platforms)"></ion-icon>
<ion-label>
<h1>{{ getPlatformName(current.metadata.platforms) }}</h1>
<h2>Last Active: {{ current['last-active'] | date : 'medium' }}</h2>
<p>{{ current['user-agent'] }}</p>
@@ -28,8 +51,8 @@
[id]="session.key"
*ngIf="session.key !== sessionInfo.current"
>
<ion-icon slot="start" [name]="getPlatformIcon(session.value.metadata.platforms)"></ion-icon>
<ion-label class="ion-text-wrap">
<ion-icon slot="start" size="large" [name]="getPlatformIcon(session.value.metadata.platforms)"></ion-icon>
<ion-label>
<h1>{{ getPlatformName(session.value.metadata.platforms) }}</h1>
<h2>Last Active: {{ session.value['last-active'] | date : 'medium' }}</h2>
<p>{{ session.value['user-agent'] }}</p>

View File

@@ -4,38 +4,62 @@
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>SSH Keys</ion-title>
<ion-buttons slot="end">
<ion-button (click)="serverConfig.presentAlert('ssh')">
<ion-icon slot="icon-only" name="add-outline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-top">
<text-spinner *ngIf="loading" text="Loading Keys"></text-spinner>
<ion-item-group *ngIf="!loading">
<!-- about -->
<!-- always -->
<ion-item-group>
<ion-item>
<ion-label class="ion-text-wrap">
<p class="ion-padding-bottom">About</p>
<h2>Adding an SSH key to your Embassy can be useful for advanced usage from the command line, as well as for debugging purposes.</h2>
<ion-label>
<h2>
Adding SSH keys to your Embassy is useful for command line access, as well as for debugging purposes.
<a [href]="docsUrl" target="_blank">View instructions</a>
</h2>
</ion-label>
</ion-item>
<ion-item [href]="docsUrl" target="_blank" detail="false">
<ion-icon slot="start" name="list-outline"></ion-icon>
<ion-label>View Instructions</ion-label>
</ion-item>
<ion-item-divider>Saved Keys</ion-item-divider>
<ion-item *ngFor="let ssh of sshKeys | keyvalue : asIsOrder">
<ion-label class="ion-text-wrap">
{{ ssh.value.alg }} {{ ssh.key }} {{ ssh.value.hostname }}
</ion-label>
<ion-button slot="end" fill="clear" (click)="presentAlertDelete(ssh.key)">
<ion-icon slot="icon-only" name="close"></ion-icon>
</ion-button>
<ion-item button detail="false" (click)="serverConfig.presentInputModal('ssh')">
<ion-icon slot="start" name="add" size="large"></ion-icon>
<ion-label>Add new key</ion-label>
</ion-item>
<!-- loading -->
<ng-container *ngIf="loading">
<ion-item *ngFor="let entry of ['', '']">
<ion-button slot="start" fill="clear">
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
</ion-button>
<ion-label>
<ion-skeleton-text animated style="width: 15%; height: 20px; margin-bottom: 12px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 50%; margin-bottom: 18px;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 20%;"></ion-skeleton-text>
</ion-label>
<ion-button slot="end" fill="clear">
<ion-skeleton-text animated style="width: 80px; border-radius: 0"></ion-skeleton-text>
</ion-button>
</ion-item>
</ng-container>
<!-- not loading -->
<ng-container *ngIf="!loading">
<ion-item *ngFor="let ssh of sshKeys | keyvalue : asIsOrder">
<ion-icon slot="start" name="key-outline" size="large"></ion-icon>
<ion-label>
<h1>{{ ssh.value.hostname }}</h1>
<h2>{{ ssh.value['created-at'] | date: 'short' }}</h2>
<p>{{ ssh.value.alg }} {{ ssh.key }}</p>
</ion-label>
<ion-button slot="end" fill="clear" color="danger" (click)="presentAlertDelete(ssh.key)">
<ion-icon slot="start" name="close"></ion-icon>
Remove
</ion-button>
</ion-item>
</ng-container>
</ion-item-group>
</ion-content>

View File

@@ -4,11 +4,6 @@
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Create Backup</ion-title>
<ion-buttons slot="end">
<ion-button (click)="doRefresh()">
<ion-icon slot="icon-only" name="refresh-outline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
@@ -18,45 +13,33 @@
<ng-template #loaded>
<ion-item class="ion-margin-bottom">
<ion-label class="ion-text-wrap">
<p class="ion-padding-bottom">About</p>
<ion-label>
<h2>
Create frequent backups of your Embassy to avoid loss of data.
Select the drive where you want to create a backup of your Embassy, including all your installed services.
</h2>
</ion-label>
</ion-item>
<ion-item-divider>Select Backup Drive</ion-item-divider>
<ion-item *ngIf="allPartitionsMounted">
<ion-text *ngIf="type === 'create'" class="ion-text-wrap" color="warning">No partitions available. To begin a backup, insert a storage device into your Embassy.</ion-text>
<ion-text *ngIf="type === 'restore'" class="ion-text-wrap" color="warning">No partitions available. Insert the storage device containing the backup you wish to restore.</ion-text>
<ion-text class="ion-text-wrap" color="warning">No partitions available. To begin a backup, insert a storage device into your Embassy.</ion-text>
</ion-item>
<ion-card *ngFor="let disk of disks | keyvalue">
<ion-card-header>
<ion-card-title>
{{ disk.value.size }}
</ion-card-title>
<ion-card-subtitle>
{{ disk.key }}
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-item-group>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>
<h2>{{ partition.value.label || partition.key }} ({{ partition.value.size || 'unknown size' }})</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</ion-item-group>
</ion-card-content>
</ion-card>
<ion-item-group>
<div *ngFor="let disk of disks | keyvalue">
<ion-item-divider>{{ disk.key }} - {{ disk.value.size }}</ion-item-divider>
<ion-item button *ngFor="let partition of disk.value.partitions | keyvalue" [disabled]="partition.value['is-mounted']" (click)="presentModal(partition.key)">
<ion-icon slot="start" name="save-outline" size="large"></ion-icon>
<ion-label>
<h1>{{ partition.value.label || partition.key }}</h1>
<h2>{{ partition.value.size || 'unknown size' }}</h2>
<p *ngIf="!partition.value['is-mounted']; else unavailable"><ion-text color="success">Available</ion-text></p>
<ng-template #unavailable>
<p><ion-text color="danger">Unavailable</ion-text></p>
</ng-template>
</ion-label>
</ion-item>
</div>
</ion-item-group>
</ng-template>
</ion-content>

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
import { LoadingController, ModalController } from '@ionic/angular'
import { ModalController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { BackupConfirmationComponent } from 'src/app/modals/backup-confirmation/backup-confirmation.component'
import { DiskInfo } from 'src/app/services/api/api.types'
@@ -19,7 +19,6 @@ export class ServerBackupPage {
private readonly modalCtrl: ModalController,
private readonly embassyApi: ApiService,
private readonly errToast: ErrorToastService,
private readonly loadingCtrl: LoadingController,
) { }
ngOnInit () {
@@ -45,36 +44,21 @@ export class ServerBackupPage {
async presentModal (logicalname: string): Promise<void> {
const m = await this.modalCtrl.create({
componentProps: {
type: 'backup',
title: 'Create Backup',
message: `Enter your master password to create an encrypted backup of your Embassy and all its installed services.`,
label: 'Password',
useMask: true,
buttonText: 'Create Backup',
submitFn: async (value: string) => await this.create(logicalname, value),
},
cssClass: 'alertlike-modal',
component: BackupConfirmationComponent,
backdropDismiss: false,
})
m.onWillDismiss().then(res => {
const data = res.data
if (data.cancel) return
this.create(logicalname, data.password)
})
return await m.present()
}
private async create (logicalname: string, password: string): Promise<void> {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Starting backup...',
cssClass: 'loader',
})
await loader.present()
try {
await this.embassyApi.createBackup({ logicalname, password })
} catch (e) {
this.errToast.present(e)
} finally {
loader.dismiss()
}
await this.embassyApi.createBackup({ logicalname, password })
}
}

View File

@@ -8,7 +8,7 @@
</ion-header>
<ion-content class="ion-padding">
<skeleton-list *ngIf="loading; else loaded" groups="3"></skeleton-list>
<skeleton-list *ngIf="loading; else loaded" groups="2"></skeleton-list>
<ng-template #loaded>
<ion-item-group *ngFor="let metricGroup of metrics | keyvalue : asIsOrder">

View File

@@ -40,7 +40,7 @@
</ion-button>
</ion-item>
<ion-item-divider>Specs</ion-item-divider>
<ion-item-divider>Hardware Specs</ion-item-divider>
<ion-item *ngFor="let spec of server.specs | keyvalue : asIsOrder">
<ion-label>

View File

@@ -15,7 +15,7 @@
<ion-content class="ion-padding-top">
<ion-item-group>
<ion-item>
<ion-label class="ion-text-wrap">
<ion-label>
<p style="padding-bottom: 6px;">About</p>
<h2>Embassy will automatically connect to saved WiFi networks when they are available, allowing you to remove the Ethernet cable.</h2>
</ion-label>