styling etc

This commit is contained in:
Matt Hill
2021-06-28 14:36:57 -06:00
committed by Aiden McClelland
parent 517251d672
commit a2103d4307
49 changed files with 519 additions and 539 deletions

View File

@@ -27,6 +27,12 @@ const routes: Routes = [
canActivate: [AuthGuard, UnmaintenanceGuard],
loadChildren: () => import('./pages/maintenance/maintenance.module').then(m => m.MaintenancePageModule),
},
{
path: 'marketplace',
canActivate: [AuthGuard, MaintenanceGuard],
canActivateChild: [AuthGuard, MaintenanceGuard],
loadChildren: () => import('./pages/marketplace-routes/marketplace-routing.module').then(m => m.MarketplaceRoutingModule),
},
{
path: 'notifications',
canActivate: [AuthGuard, MaintenanceGuard],

View File

@@ -1,5 +1,5 @@
.selected {
--background: linear-gradient(120deg, #1e1e1e -1%, var(--ion-color-start9) 96%);
--background: linear-gradient(120deg, #1e1e1e -1%, var(--ion-color-start9) 100%);
}
.menu-style {
@@ -10,4 +10,8 @@
.selected-badge {
background-color: #1e1e1e;
}
ion-split-pane {
--side-max-width: 280px;
}

View File

@@ -28,7 +28,7 @@ export class AppComponent {
appPages = [
{
title: 'Services',
url: '/services/installed',
url: '/services',
icon: 'grid-outline',
},
{
@@ -38,7 +38,7 @@ export class AppComponent {
},
{
title: 'Marketplace',
url: '/services/marketplace',
url: '/marketplace',
icon: 'storefront-outline',
},
{

View File

@@ -1,24 +1,9 @@
<p *ngIf="size === 'small'" style="margin: 0 0 4px 0;">
<ion-text [color]="color">{{ display }}</ion-text>
<p
[style.color]="'var(--ion-color-' + color + ')'"
[style.font-size]="size"
[style.font-style]="style"
[style.font-weight]="weight"
>
{{ display }}
<ion-spinner *ngIf="showDots" class="dots dots-small" name="dots" [color]="color"></ion-spinner>
</p>
<p *ngIf="size === 'italics-small'" style="margin: 0 0 4px 0; font-style: italic;">
<ion-text [color]="color">{{ display }}</ion-text>
<ion-spinner *ngIf="showDots" class="dots dots-small" name="dots" [color]="color"></ion-spinner>
</p>
<h3 *ngIf="size === 'medium'">
<ion-text [color]="color">{{ display }}</ion-text>
<ion-spinner *ngIf="showDots" class="dots dots-medium" name="dots" [color]="color"></ion-spinner>
</h3>
<h1 *ngIf="size === 'large'">
<ion-text [color]="color">{{ display }}</ion-text>
<ion-spinner *ngIf="showDots" class="dots" name="dots" [color]="color"></ion-spinner>
</h1>
<h1 *ngIf="size === 'bold-large'" style="font-size: 18px; font-weight: 500">
<ion-text [color]="color">{{ display }}</ion-text>
<ion-spinner *ngIf="showDots" class="dots" name="dots" [color]="color"></ion-spinner>
</h1>

View File

@@ -1,17 +1,5 @@
.icon-small {
width: auto;
height: 14px;
padding-left: 6px;
}
.icon-medium {
width: auto;
height: 18px;
padding-left: 8px;
}
.icon-large {
width: auto;
height: 24px;
padding-left: 12px;
p {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@@ -11,7 +11,9 @@ import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
export class StatusComponent {
@Input() pkg: PackageDataEntry
@Input() connection: ConnectionState
@Input() size: 'small' | 'medium' | 'large' | 'italics-small' | 'bold-large' = 'large'
@Input() size?: 'small' | 'medium' | 'large' = 'large'
@Input() style?: string = 'regular'
@Input() weight?: string = 'normal'
display = ''
color = ''
showDots = false

View File

@@ -89,7 +89,7 @@ export class AppActionsPage {
)
if (data.cancelled) return
return this.navCtrl.navigateRoot('/services/installed')
return this.navCtrl.navigateRoot('/services')
}
private async executeAction (pkgId: string, actionId: string) {

View File

@@ -1,161 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Listing</ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-bottom">
<ion-spinner *ngIf="!aaService.pkgs[pkgId]" class="center" name="lines" color="warning"></ion-spinner>
<ng-container *ngIf="aaService.pkgs[pkgId] as pkg">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ng-container *ngrxLet="patch.watch$('package-data', pkgId) as localPkg">
<ion-item-group>
<ion-item lines="none">
<ion-avatar slot="start">
<img [src]="pkg.icon" />
</ion-avatar>
<ion-label class="ion-text-wrap">
<h1 style="font-family: 'Montserrat'">{{ pkg.manifest.title }}</h1>
<h3>{{ pkg.manifest.version | displayEmver }}</h3>
<!-- no localPkg -->
<h3 *ngIf="!localPkg; else local">
<ion-text color="medium">Not Installed</ion-text>
</h3>
<!-- localPkg -->
<ng-template #local>
<h3 *ngIf="localPkg.state !== PackageState.Installed; else installed">
<!-- installing, updating, removing -->
<ion-text [color]="localPkg.state === PackageState.Removing ? 'danger' : 'primary'">{{ localPkg.state }}</ion-text>
<ion-spinner class="dots dots-medium" name="dots" [color]="localPkg.state === PackageState.Removing ? 'danger' : 'primary'"></ion-spinner>
</h3>
<!-- installed -->
<ng-template #installed>
<h3>
<ion-text color="medium">Installed at {{ localPkg.installed.manifest.version | displayEmver }}</ion-text>
</h3>
</ng-template>
</ng-template>
</ion-label>
</ion-item>
</ion-item-group>
<!-- no localPkg -->
<ion-button *ngIf="!localPkg; else localPkg2" class="main-action-button" expand="block" fill="outline" color="success" (click)="install()">
Install
</ion-button>
<!-- localPkg -->
<ng-template #localPkg2>
<!-- not removing -->
<ng-container *ngIf="localPkg.state !== PackageState.Removing">
<ion-button class="main-action-button" expand="block" fill="outline" [routerLink]="['/services', 'installed', pkgId]">
Go to Service
</ion-button>
<!-- not installing or updating -->
<ng-container *ngIf="localPkg.state === PackageState.Installed">
<ion-button *ngIf="(localPkg.installed.manifest.version | compareEmver : pkg.manifest.version) === -1" class="main-action-button" expand="block" fill="outline" color="success" (click)="update('update')">
Update to {{ pkg.manifest.version | displayEmver }}
</ion-button>
<ion-button *ngIf="(localPkg.installed.manifest.version | compareEmver : pkg.manifest.version) === 1" class="main-action-button" expand="block" fill="outline" color="warning" (click)="update('downgrade')">
Downgrade to {{ pkg.manifest.version | displayEmver }}
</ion-button>
</ng-container>
</ng-container>
</ng-template>
</ng-container>
<!-- recommendation -->
<ion-item *ngIf="rec && showRec" class="rec-item">
<ion-label class="ion-text-wrap">
<h2 style="display: flex; align-items: center;">
<ion-avatar style="height: 3vh; width: 3vh; margin: 5px" slot="start">
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
</ion-avatar>
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
</h2>
<div style="margin: 7px 5px;">
<p style="color: var(--ion-color-dark); font-size: small">{{ rec.description }}</p>
<p *ngIf="pkg.manifest.version | satisfiesEmver: rec.version" class="recommendation-text">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is compatible.</p>
<p *ngIf="!(pkg.manifest.version | satisfiesEmver: rec.version)" class="recommendation-text recommendation-error">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is NOT compatible.</p>
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
<ion-icon name="close-outline"></ion-icon>
</ion-button>
</div>
</ion-label>
</ion-item>
<ion-item-group>
<!-- release notes -->
<ion-item-divider style="color: var(--ion-color-dark); font-weight: bold;">
New in {{ pkg.manifest.version | displayEmver }}
<ion-button [routerLink]="['notes']" style="position: absolute; right: 10px;" fill="clear" color="primary" >
Version History
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
</ion-button>
</ion-item-divider>
<ion-item lines="none">
<ion-label style="display: flex; align-items: center; justify-content: space-between;" class="ion-text-wrap" >
<div id='release-notes' [innerHTML]="pkg.manifest['release-notes'] | markdown"></div>
</ion-label>
</ion-item>
<!-- description -->
<ion-item-divider class="divider">
<ion-text color="dark">Description</ion-text>
</ion-item-divider>
<ion-item lines="none">
<ion-label class="ion-text-wrap">
<ion-text color="dark">
<h5>{{ pkg.manifest.description.long }}</h5>
</ion-text>
</ion-label>
</ion-item>
<!-- dependencies -->
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
<ion-item-divider class="divider">
<ion-text color="dark">Service Dependencies</ion-text>
<ion-button style="position: relative; right: 10px;" size="small" fill="clear" color="dark" (click)="presentPopover(depDefintion, $event)">
<ion-icon name="help-circle-outline"></ion-icon>
</ion-button>
</ion-item-divider>
<div *ngFor="let dep of pkg.manifest.dependencies | keyvalue">
<ion-item *ngIf="!dep.value.optional" class="dependency-item">
<ion-avatar slot="start">
<img [src]="pkg['dependency-metadata'][dep.key].icon" />
</ion-avatar>
<ion-label class="ion-text-wrap" style="padding: 1vh; padding-left: 2vh">
<h4 style="font-family: 'Montserrat'">
{{ pkg['dependency-metadata'][dep.key].title }}
<span *ngIf="dep.value.recommended" style="font-family: 'Open Sans'; font-size: small; color: var(--ion-color-dark)"> (recommended)</span>
</h4>
<p style="font-size: small">{{ dep.value.version | displayEmver }}</p>
</ion-label>
</ion-item>
<ion-item style="margin-bottom: 10px" *ngIf="dep.value.description" lines="none">
<div style="font-size: small; color: var(--ion-color-dark)" [innerHtml]="dep.value.description"></div>
</ion-item>
</div>
</ng-container>
<!-- versions -->
<ion-item-divider></ion-item-divider>
<ion-item lines="none" button (click)="presentAlertVersions()">
<ion-icon color="dark" slot="start" name="file-tray-stacked-outline"></ion-icon>
<ion-label color="dark">Other versions</ion-label>
</ion-item>
</ion-item-group>
</ng-container>
</ion-content>

View File

@@ -1,24 +0,0 @@
.recommendation-text {
font-style: italic;
}
.recommendation-error {
color: var(--ion-color-danger);
}
.main-action-button {
margin: 20px 5px 20px 5px;
}
.divider {
margin-top: 15px;
color: var(--ion-color-medium);
font-size: medium;
padding-left: 10px;
font-weight: unset;
}
#release-notes {
overflow: auto;
max-height: 160px;
}

View File

@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppInstalledListPage } from './app-installed-list.page'
import { AppListPage } from './app-list.page'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
@@ -10,7 +10,7 @@ import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/b
const routes: Routes = [
{
path: '',
component: AppInstalledListPage,
component: AppListPage,
},
]
@@ -24,7 +24,7 @@ const routes: Routes = [
BadgeMenuComponentModule,
],
declarations: [
AppInstalledListPage,
AppListPage,
],
})
export class AppInstalledListPageModule { }
export class AppListPageModule { }

View File

@@ -15,7 +15,7 @@
<h2>Welcome to your <span style="font-style: italic; color: var(--ion-color-start9)">Embassy</span></h2>
<p class="ion-text-wrap">Get started by installing your first service.</p>
</div>
<ion-button [routerLink]="['/services','marketplace']" style="width: 50%;" fill="outline">
<ion-button [routerLink]="['/marketplace']" style="width: 50%;" fill="outline">
<ion-icon slot="start" name="storefront-outline"></ion-icon>
Marketplace
</ion-button>
@@ -24,10 +24,10 @@
<ng-template #list>
<ion-grid>
<ion-row *ngIf="connectionService.monitor$() | ngrxPush as connection">
<ion-col *ngFor="let pkg of pkgs | keyvalue : asIsOrder" sizeXs="4" sizeSm="3" sizeMd="2" sizeLg="2">
<ion-card class="installed-card" style="position:relative" [routerLink]="['/services', 'installed', (pkg.value | manifest).id]">
<ion-col *ngFor="let pkg of pkgs | keyvalue : asIsOrder" sizeXs="4" sizeSm="3" sizeLg="3" sizeXl="2">
<ion-card class="installed-card" style="position:relative" [routerLink]="['/services', (pkg.value | manifest).id]">
<div class="launch-container" *ngIf="pkg.value | hasUi">
<div class="launch-button-triangle" (click)="launchUi(pkg.value, $event)" [class.disabled]="!(pkg.value | isLaunchable)">
<div class="launch-button-triangle" (click)="launchUi(pkg.value, $event)" [class.launch-disabled]="!(pkg.value | isLaunchable)">
<ion-icon name="rocket-outline"></ion-icon>
</div>
</div>
@@ -40,8 +40,8 @@
<img class="bulb-off" *ngIf="pkg.value | displayBulb: 'off' : connection" src="assets/img/off-bulb.png"/>
<ion-card-header>
<status [pkg]="pkg.value" [connection]="connection" size="small"></status>
<p>{{ (pkg.value | manifest).title }}</p>
<status [pkg]="pkg.value" [connection]="connection" size="calc(4px + .7vw)" weight="bold"></status>
<ion-card-title>{{ (pkg.value | manifest).title }}</ion-card-title>
</ion-card-header>
</ion-card>
</ion-col>

View File

@@ -5,21 +5,16 @@
text-align: center;
ion-card-header {
padding: 0;
padding: 0 10px;
status {
font-size: 9px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
p {
font-family: 'Montserrat';
font-size: 11px;
ion-card-title {
font-size: calc(8px + .7vw);
color: white;
margin: 0px 12px 8px 12px;
margin: 10px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -72,7 +67,7 @@
}
}
.disabled {
.launch-disabled {
pointer-events: none;
border-color: transparent;
&:hover {

View File

@@ -5,11 +5,11 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { PackageDataEntry } from 'src/app/models/patch-db/data-model'
@Component({
selector: 'app-installed-list',
templateUrl: './app-installed-list.page.html',
styleUrls: ['./app-installed-list.page.scss'],
selector: 'app-list',
templateUrl: './app-list.page.html',
styleUrls: ['./app-list.page.scss'],
})
export class AppInstalledListPage {
export class AppListPage {
pkgs: PackageDataEntry[] = []
constructor (

View File

@@ -6,7 +6,6 @@ import { copyToClipboard } from 'src/app/util/web.util'
import { AlertController, NavController, PopoverController, ToastController } from '@ionic/angular'
import { PackageProperties } from 'src/app/util/properties.util'
import { QRComponent } from 'src/app/components/qr/qr.component'
import { PropertyStore } from './property-store'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import * as JsonPointer from 'json-pointer'
import { FEStatus } from 'src/app/services/pkg-status-rendering.service'
@@ -34,7 +33,6 @@ export class AppPropertiesPage {
private readonly alertCtrl: AlertController,
private readonly toastCtrl: ToastController,
private readonly popoverCtrl: PopoverController,
private readonly propertyStore: PropertyStore,
private readonly navCtrl: NavController,
public readonly patch: PatchDbModel,
) { }
@@ -75,7 +73,7 @@ export class AppPropertiesPage {
}
async goToNested (key: string): Promise<any> {
this.navCtrl.navigateForward(`/services/installed/${this.pkgId}/properties`, {
this.navCtrl.navigateForward(`/services/${this.pkgId}/properties`, {
queryParams: {
pointer: `${this.pointer || ''}/${key}/value`,
},

View File

@@ -1,15 +0,0 @@
import { Injectable } from '@angular/core'
import { BehaviorSubject } from 'rxjs'
import { PackageProperties } from '../../../util/properties.util'
@Injectable({
providedIn: 'root',
})
export class PropertyStore {
properties$: BehaviorSubject<PackageProperties> = new BehaviorSubject({ })
watch$ () { return this.properties$.asObservable() }
update (properties: PackageProperties): void {
this.properties$.next(properties)
}
}

View File

@@ -2,18 +2,17 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppInstalledShowPage } from './app-installed-show.page'
import { AppShowPage } from './app-show.page'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
import { SharingModule } from 'src/app/modules/sharing.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { InstallWizardComponentModule } from 'src/app/components/install-wizard/install-wizard.component.module'
import { InformationPopoverComponentModule } from 'src/app/components/information-popover/information-popover.component.module'
const routes: Routes = [
{
path: '',
component: AppInstalledShowPage,
component: AppShowPage,
},
]
@@ -27,9 +26,8 @@ const routes: Routes = [
PwaBackComponentModule,
BadgeMenuComponentModule,
InstallWizardComponentModule,
InformationPopoverComponentModule,
],
declarations: [AppInstalledShowPage],
declarations: [AppShowPage],
})
export class AppInstalledShowPageModule { }
export class AppShowPageModule { }

View File

@@ -41,23 +41,21 @@
</ion-label>
</ion-item>
<ion-item class="no-cushion-item" lines="none" style="margin-bottom: 10px;">
<ion-label class="status-readout">
<status size="bold-large" [pkg]="pkg" [connection]="connection"></status>
<ion-button *ngIf="status === FeStatus.NeedsConfig" expand="block" fill="outline" [routerLink]="['config']">
Configure
</ion-button>
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : status" expand="block" fill="outline" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button *ngIf="status === FeStatus.DependencyIssue" expand="block" fill="outline" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button *ngIf="status === FeStatus.Stopped" expand="block" fill="outline" color="success" (click)="tryStart()">
Start
</ion-button>
</ion-label>
</ion-item>
<div class="status-readout">
<status size="large" weight="bold" [pkg]="pkg" [connection]="connection"></status>
<ion-button *ngIf="status === FeStatus.NeedsConfig" expand="block" fill="outline" [routerLink]="['config']">
Configure
</ion-button>
<ion-button *ngIf="[FeStatus.Running, FeStatus.StartingUp, FeStatus.NeedsAttention] | includes : status" expand="block" fill="outline" color="danger" (click)="stop()">
Stop
</ion-button>
<ion-button *ngIf="status === FeStatus.DependencyIssue" expand="block" fill="outline" (click)="scrollToRequirements()">
Fix
</ion-button>
<ion-button *ngIf="status === FeStatus.Stopped" expand="block" fill="outline" color="success" (click)="tryStart()">
Start
</ion-button>
</div>
<ion-button size="small" *ngIf="pkg | hasUi" [disabled]="!(pkg | isLaunchable)" class="launch-button" expand="block" (click)="launchUiTab()">
Launch Web Interface
@@ -69,11 +67,11 @@
<ion-grid class="ion-text-center" style="margin: 0 6px;">
<ion-row>
<ion-col *ngFor="let button of buttons" sizeMd="4" sizeSm="6" sizeXs="6">
<ion-button style="width: 100% !important; min-height: 120px; --background: #2f4858;" [disabled]="button.disabled | includes : status" (click)="button.action()">
<ion-button style="width: 100%; min-height: 120px;" color="light" [disabled]="button.disabled | includes : status" (click)="button.action()">
<div>
<ion-icon size="large" [name]="button.icon" style="color: #fbe6ea"></ion-icon>
<ion-icon size="large" [name]="button.icon"></ion-icon>
<br/><br/>
<ion-text style="color: #fbe6ea">{{ button.title }}</ion-text>
{{ button.title }}
</div>
</ion-button>
</ion-col>
@@ -101,7 +99,7 @@
<p style="padding-top: 2px; position: relative; font-style: italic; font-size: smaller"><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>
</ion-label>
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" [routerLink]="['/services', 'installed', dep.key]" color="primary" fill="outline" style="font-size: x-small">
<ion-button *ngIf="!pkg.installed.status['dependency-errors'][dep.key] || (pkg.installed.status['dependency-errors'][dep.key] && [DependencyErrorType.InterfaceHealthChecksFailed, DependencyErrorType.HealthChecksFailed] | includes : pkg.installed.status['dependency-errors'][dep.key].type)" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
View
</ion-button>
@@ -111,7 +109,7 @@
</ion-button>
<ng-container *ngIf="localDep && localDep.state === PackageState.Installed">
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" [routerLink]="['/services', 'installed', dep.key]" color="primary" fill="outline" style="font-size: x-small">
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.NotRunning" slot="end" size="small" [routerLink]="['/services', dep.key]" color="primary" fill="outline" style="font-size: x-small">
Start
</ion-button>
<ion-button *ngIf="pkg.installed.status['dependency-errors'][dep.key].type === DependencyErrorType.IncorrectVersion" slot="end" size="small" (click)="fixDep('update', dep.key)" color="primary" fill="outline" style="font-size: x-small">

View File

@@ -1,15 +1,3 @@
.full-width {
margin: 10px;
}
.about-attribute {
font-size: small;
}
.about-attribute-value {
font-size: small;
}
.less-large {
font-size: 20px !important;
}
@@ -23,9 +11,10 @@
}
.status-readout {
--background: transparent;
display: flex;
justify-content: space-between;
padding: 4px 10px;
padding: 0 10px;
border-radius: 10px;
align-items: center;
background: var(--ion-background-color);
@@ -35,10 +24,6 @@
border-color: #404040;
}
.no-cushion-item {
--background: transparent; --padding-start: 0px; --inner-padding-end: 0px; --padding-end: 0px;
}
.launch-button {
--background: rgb(70 193 255 / 75%);
--background-hover: rgb(70 193 255);

View File

@@ -1,5 +1,5 @@
import { Component, ViewChild } from '@angular/core'
import { AlertController, NavController, ModalController, IonContent, PopoverController } from '@ionic/angular'
import { AlertController, NavController, ModalController, IonContent } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { ActivatedRoute, NavigationExtras } from '@angular/router'
import { chill, isEmptyObject } from 'src/app/util/misc.util'
@@ -7,7 +7,6 @@ import { LoaderService } from 'src/app/services/loader.service'
import { Observable, of, Subscription } from 'rxjs'
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { InformationPopoverComponent } from 'src/app/components/information-popover/information-popover.component'
import { ConfigService, getManifest } from 'src/app/services/config.service'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { DependencyErrorConfigUnsatisfied, DependencyErrorNotInstalled, DependencyErrorType, Manifest, PackageDataEntry, PackageState } from 'src/app/models/patch-db/data-model'
@@ -16,11 +15,11 @@ import { ConnectionService } from 'src/app/services/connection.service'
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
@Component({
selector: 'app-installed-show',
templateUrl: './app-installed-show.page.html',
styleUrls: ['./app-installed-show.page.scss'],
selector: 'app-show',
templateUrl: './app-show.page.html',
styleUrls: ['./app-show.page.scss'],
})
export class AppInstalledShowPage {
export class AppShowPage {
error: string
pkgId: string
pkg: PackageDataEntry
@@ -43,7 +42,6 @@ export class AppInstalledShowPage {
private readonly modalCtrl: ModalController,
private readonly apiService: ApiService,
private readonly wizardBaker: WizardBaker,
private readonly popoverController: PopoverController,
private readonly config: ConfigService,
public readonly patch: PatchDbModel,
public readonly connectionService: ConnectionService,
@@ -120,20 +118,6 @@ export class AppInstalledShowPage {
}
}
async presentPopover (information: string, ev: any) {
const popover = await this.popoverController.create({
component: InformationPopoverComponent,
event: ev,
translucent: false,
showBackdrop: true,
backdropDismiss: true,
componentProps: {
information,
},
})
return await popover.present()
}
scrollToRequirements () {
const el = document.getElementById('dependencies')
if (!el) return
@@ -170,7 +154,7 @@ export class AppInstalledShowPage {
state: { installRec },
}
await this.navCtrl.navigateForward(`/services/marketplace/${depId}`, navigationExtras)
await this.navCtrl.navigateForward(`/marketplace/${depId}`, navigationExtras)
}
private async configureDep (depId: string): Promise<void> {
@@ -189,7 +173,7 @@ export class AppInstalledShowPage {
state: { configRecommendation },
}
await this.navCtrl.navigateForward(`/services/installed/${depId}/config`, navigationExtras)
await this.navCtrl.navigateForward(`/services/${depId}/config`, navigationExtras)
}
private async presentAlertStart (message: string): Promise<void> {
@@ -300,7 +284,7 @@ export class AppInstalledShowPage {
disabled: [],
},
{
action: () => this.navCtrl.navigateForward(['/services', 'marketplace', this.pkgId], { relativeTo: this.route }),
action: () => this.navCtrl.navigateForward(['/marketplace', this.pkgId], { relativeTo: this.route }),
title: 'Marketplace Listing',
icon: 'storefront-outline',
color: 'danger',

View File

@@ -4,69 +4,57 @@ import { Routes, RouterModule } from '@angular/router'
const routes: Routes = [
{
path: '',
redirectTo: 'installed',
redirectTo: 'list',
pathMatch: 'full',
},
{
path: 'installed',
loadChildren: () => import('./app-installed-list/app-installed-list.module').then(m => m.AppInstalledListPageModule),
path: 'list',
loadChildren: () => import('./app-list/app-list.module').then(m => m.AppListPageModule),
},
{
path: 'installed/:pkgId',
loadChildren: () => import('./app-installed-show/app-installed-show.module').then(m => m.AppInstalledShowPageModule),
path: ':pkgId',
loadChildren: () => import('./app-show/app-show.module').then(m => m.AppShowPageModule),
},
{
path: 'installed/:pkgId/actions',
path: ':pkgId/actions',
loadChildren: () => import('./app-actions/app-actions.module').then(m => m.AppActionsPageModule),
},
{
path: 'installed/:pkgId/config',
path: ':pkgId/config',
loadChildren: () => import('./app-config/app-config.module').then(m => m.AppConfigPageModule),
},
{
path: 'installed/:pkgId/config/:edit',
path: ':pkgId/config/:edit',
loadChildren: () => import('./app-config/app-config.module').then(m => m.AppConfigPageModule),
},
{
path: 'installed/:pkgId/instructions',
path: ':pkgId/instructions',
loadChildren: () => import('./app-instructions/app-instructions.module').then(m => m.AppInstructionsPageModule),
},
{
path: 'installed/:pkgId/interfaces',
path: ':pkgId/interfaces',
loadChildren: () => import('./app-interfaces/app-interfaces.module').then(m => m.AppInterfacesPageModule),
},
{
path: 'installed/:pkgId/logs',
path: ':pkgId/logs',
loadChildren: () => import('./app-logs/app-logs.module').then(m => m.AppLogsPageModule),
},
{
path: 'installed/:pkgId/manifest',
path: ':pkgId/manifest',
loadChildren: () => import('./app-manifest/app-manifest.module').then(m => m.AppManifestPageModule),
},
{
path: 'installed/:pkgId/metrics',
path: ':pkgId/metrics',
loadChildren: () => import('./app-metrics/app-metrics.module').then(m => m.AppMetricsPageModule),
},
{
path: 'installed/:pkgId/properties',
path: ':pkgId/properties',
loadChildren: () => import('./app-properties/app-properties.module').then(m => m.AppPropertiesPageModule),
},
{
path: 'installed/:pkgId/restore',
path: ':pkgId/restore',
loadChildren: () => import('./app-restore/app-restore.module').then(m => m.AppRestorePageModule),
},
{
path: 'marketplace',
loadChildren: () => import('./app-available-list/app-available-list.module').then(m => m.AppAvailableListPageModule),
},
{
path: 'marketplace/:pkgId',
loadChildren: () => import('./app-available-show/app-available-show.module').then(m => m.AppAvailableShowPageModule),
},
{
path: 'marketplace/:pkgId/notes',
loadChildren: () => import('./app-release-notes/app-release-notes.module').then(m => m.ReleaseNotesModule),
},
]
@NgModule({

View File

@@ -8,10 +8,10 @@
</ion-header>
<ion-content>
<ion-spinner *ngIf="!aaService.pkgs[pkgId]; else loaded" class="center" name="lines" color="warning"></ion-spinner>
<ion-spinner *ngIf="!marketplaceService.pkgs[pkgId]; else loaded" class="center" name="lines" color="warning"></ion-spinner>
<ng-template #loaded>
<div *ngFor="let note of aaService.pkgs[pkgId]['release-notes'] | keyvalue : asIsOrder">
<div *ngFor="let note of marketplaceService.pkgs[pkgId]['release-notes'] | keyvalue : asIsOrder">
<ion-button (click)="setSelected(note.key)" expand="full" color="light" style="height: 50px;" >
<p style="position: absolute; left: 10px;">{{ note.key | displayEmver }}</p>
</ion-button>

View File

@@ -1,6 +1,6 @@
import { Component } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { AppAvailableService } from '../app-available.service'
import { MarketplaceService } from '../marketplace.service'
@Component({
selector: 'app-release-notes',
@@ -14,7 +14,7 @@ export class AppReleaseNotes {
constructor (
private readonly route: ActivatedRoute,
public aaService: AppAvailableService,
public aaService: MarketplaceService,
) { }
ngOnInit () {

View File

@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppAvailableListPage } from './app-available-list.page'
import { MarketplaceListPage } from './marketplace-list.page'
import { SharingModule } from '../../../modules/sharing.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
@@ -11,7 +11,7 @@ import { StatusComponentModule } from 'src/app/components/status/status.componen
const routes: Routes = [
{
path: '',
component: AppAvailableListPage,
component: MarketplaceListPage,
},
]
@@ -24,6 +24,6 @@ const routes: Routes = [
SharingModule,
BadgeMenuComponentModule,
],
declarations: [AppAvailableListPage],
declarations: [MarketplaceListPage],
})
export class AppAvailableListPageModule { }
export class MarketplaceListPageModule { }

View File

@@ -19,19 +19,7 @@
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<div class="scrollable">
<ion-button
*ngFor="let cat of data.categories"
size="small"
fill="clear"
[color]="cat === category ? 'success' : 'dark'"
(click)="switchCategory(cat)"
>
{{ cat }}
</ion-button>
</div>
<ion-card *ngIf="eos && category === 'featured'" class="eos-card" (click)="updateEos()">
<ion-card *ngIf="eos" class="eos-card" (click)="updateEos()">
<ion-card-header>
<ion-card-subtitle>Now Available...</ion-card-subtitle>
<ion-card-title>EmbassyOS Version {{ eos.version }}</ion-card-title>
@@ -41,10 +29,42 @@
</ion-card-content>
</ion-card>
<ion-spinner *ngIf="pkgsLoading; else pkgsLoaded" style="padding-top: 200px;" name="lines" color="warning"></ion-spinner>
<h2 class="ion-margin-start">Categories</h2>
<div class="scrollable">
<ion-button
*ngFor="let cat of data.categories"
fill="clear"
[color]="cat === category ? 'primary' : 'dark'"
[class.cat-selected]="cat === category"
(click)="switchCategory(cat)"
>
{{ cat }}
</ion-button>
</div>
<div *ngIf="pkgsLoading; else pkgsLoaded" style="margin-top: 64px;" class="ion-text-center">
<ion-spinner name="lines" color="warning"></ion-spinner>
</div>
<ng-template #pkgsLoaded>
<ion-card *ngFor="let pkg of pkgs" style="margin: 10px 10px;" [routerLink]="[pkg.id]">
<ion-grid style="margin: 0 6px;">
<ion-row>
<ion-col *ngFor="let pkg of pkgs" sizeXl="2" sizeLg="3" sizeMd="3" sizeSm="4" sizeXs="4">
<ion-card class="ion-text-center" style="margin: 0;" [routerLink]="['/marketplace', pkg.id]">
<img [src]="pkg.icon" style="width: 90%; margin-top: 8px;" />
<ion-card-header style="min-height: 70px; text-align: left;">
<ion-card-title>{{ pkg.title }}</ion-card-title>
</ion-card-header>
<ion-card-content style="text-align: left;">
{{ pkg.descriptionShort }}
</ion-card-content>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
<!-- <ion-card *ngFor="let pkg of pkgs" style="margin: 10px 10px;" [routerLink]="['/marketplace', pkg.id]">
<ion-item style="--inner-border-width: 0 0 .4px 0; --border-color: #525252;">
<ion-avatar style="margin-top: 8px;" slot="start">
<img [src]="pkg.icon" />
@@ -81,7 +101,7 @@
">
{{ pkg.descriptionShort }}
</ion-card-content>
</ion-card>
</ion-card> -->
</ng-template>
</ng-template>
</ion-content>

View File

@@ -8,8 +8,8 @@
.scrollable {
overflow: auto;
white-space: nowrap;
background-color: var(--ion-color-light);
margin-bottom: 16px;
// background-color: var(--ion-color-light);
height: 80px;
/* Hide scrollbar for Chrome, Safari and Opera */
::-webkit-scrollbar {
@@ -23,6 +23,11 @@
.eos-card {
--background: linear-gradient(45deg, #101010 16%, var(--ion-color-danger) 150%);
margin: 16px 10px;
margin: 0 10px 16px 10px;
cursor: pointer;
}
.cat-selected {
border-width: 0 0 1px 0;
border-style: solid;
}

View File

@@ -8,11 +8,11 @@ import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { PackageState } from 'src/app/models/patch-db/data-model'
@Component({
selector: 'app-available-list',
templateUrl: './app-available-list.page.html',
styleUrls: ['./app-available-list.page.scss'],
selector: 'marketplace-list',
templateUrl: './marketplace-list.page.html',
styleUrls: ['./marketplace-list.page.scss'],
})
export class AppAvailableListPage {
export class MarketplaceListPage {
pageLoading = true
pkgsLoading = true
error = ''

View File

@@ -0,0 +1,28 @@
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'
const routes: Routes = [
{
path: '',
redirectTo: 'browse',
pathMatch: 'full',
},
{
path: 'browse',
loadChildren: () => import('./marketplace-list/marketplace-list.module').then(m => m.MarketplaceListPageModule),
},
{
path: ':pkgId',
loadChildren: () => import('./marketplace-show/marketplace-show.module').then(m => m.MarketplaceShowPageModule),
},
{
path: ':pkgId/notes',
loadChildren: () => import('./app-release-notes/app-release-notes.module').then(m => m.ReleaseNotesModule),
},
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class MarketplaceRoutingModule { }

View File

@@ -2,19 +2,18 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { Routes, RouterModule } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { AppAvailableShowPage } from './app-available-show.page'
import { MarketplaceShowPage } from './marketplace-show.page'
import { SharingModule } from 'src/app/modules/sharing.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
import { StatusComponentModule } from 'src/app/components/status/status.component.module'
import { RecommendationButtonComponentModule } from 'src/app/components/recommendation-button/recommendation-button.component.module'
import { InstallWizardComponentModule } from 'src/app/components/install-wizard/install-wizard.component.module'
import { InformationPopoverComponentModule } from 'src/app/components/information-popover/information-popover.component.module'
const routes: Routes = [
{
path: '',
component: AppAvailableShowPage,
component: MarketplaceShowPage,
},
]
@@ -29,8 +28,7 @@ const routes: Routes = [
RecommendationButtonComponentModule,
BadgeMenuComponentModule,
InstallWizardComponentModule,
InformationPopoverComponentModule,
],
declarations: [AppAvailableShowPage],
declarations: [MarketplaceShowPage],
})
export class AppAvailableShowPageModule { }
export class MarketplaceShowPageModule { }

View File

@@ -0,0 +1,156 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>Listing</ion-title>
<ion-buttons slot="end">
<badge-menu-button></badge-menu-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-spinner *ngIf="!marketplaceService.pkgs[pkgId]" class="center" name="lines" color="warning"></ion-spinner>
<ng-container *ngIf="marketplaceService.pkgs[pkgId] as pkg">
<ion-item *ngIf="error" style="margin-bottom: 16px;">
<ion-text class="ion-text-wrap" color="danger">{{ error }}</ion-text>
</ion-item>
<ng-container *ngrxLet="patch.watch$('package-data', pkgId) as localPkg">
<ion-grid>
<ion-row>
<ion-col sizeXl="9" sizeLg="9" sizeMd="9" sizeSm="12" sizeXs="12">
<div class="header">
<img [src]="pkg.icon" />
<div class="header-text">
<h1 class="header-title">{{ pkg.manifest.title }}</h1>
<p class="header-version">{{ pkg.manifest.version | displayEmver }}</p>
<div class="header-status">
<!-- no localPkg -->
<p *ngIf="!localPkg; else local">
<ion-text color="medium">Not Installed</ion-text>
</p>
<!-- localPkg -->
<ng-template #local>
<p *ngIf="localPkg.state !== PackageState.Installed; else installed">
<!-- installing, updating, removing -->
<ion-text [color]="localPkg.state === PackageState.Removing ? 'danger' : 'primary'">{{ localPkg.state }}</ion-text>
<ion-spinner class="dots dots-medium" name="dots" [color]="localPkg.state === PackageState.Removing ? 'danger' : 'primary'"></ion-spinner>
</p>
<!-- installed -->
<ng-template #installed>
<p>
<ion-text color="medium">Installed at {{ localPkg.installed.manifest.version | displayEmver }}</ion-text>
</p>
</ng-template>
</ng-template>
</div>
</div>
</div>
</ion-col>
<ion-col sizeXl="3" sizeLg="3" sizeMd="3" sizeSm="12" sizeXs="12" class="ion-align-self-center">
<!-- no localPkg -->
<ion-button *ngIf="!localPkg; else localPkg2" class="main-action-button" expand="block" (click)="install()">
Install
</ion-button>
<!-- localPkg -->
<ng-template #localPkg2>
<!-- not installing, updating, or removing -->
<ng-container *ngIf="localPkg.state === PackageState.Installed">
<ion-button *ngIf="(localPkg.installed.manifest.version | compareEmver : pkg.manifest.version) === -1" class="main-action-button" expand="block" (click)="update('update')">
Update
</ion-button>
<ion-button *ngIf="(localPkg.installed.manifest.version | compareEmver : pkg.manifest.version) === 1" class="main-action-button" expand="block" color="warning" (click)="update('downgrade')">
Downgrade
</ion-button>
</ng-container>
</ng-template>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
<!-- recommendation -->
<ion-item *ngIf="rec && showRec" class="rec-item">
<ion-label class="ion-text-wrap">
<h2 style="display: flex; align-items: center;">
<ion-avatar style="height: 3vh; width: 3vh; margin: 5px" slot="start">
<img [src]="rec.dependentIcon" [alt]="rec.dependentTitle"/>
</ion-avatar>
<ion-text style="margin: 5px; font-family: 'Montserrat'; font-size: smaller;">{{ rec.dependentTitle }}</ion-text>
</h2>
<div style="margin: 7px 5px;">
<p style="color: var(--ion-color-dark); font-size: small">{{ rec.description }}</p>
<p *ngIf="pkg.manifest.version | satisfiesEmver: rec.version" class="recommendation-text">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is compatible.</p>
<p *ngIf="!(pkg.manifest.version | satisfiesEmver: rec.version)" class="recommendation-text recommendation-error">{{ pkg.manifest.title }} version {{ pkg.manifest.version | displayEmver }} is NOT compatible.</p>
<ion-button style="position: absolute; right: 0; top: 0" color="primary" fill="clear" (click)="dismissRec()">
<ion-icon name="close-outline"></ion-icon>
</ion-button>
</div>
</ion-label>
</ion-item>
<ion-item-group>
<!-- release notes -->
<ion-item-divider>
New in {{ pkg.manifest.version | displayEmver }}
<ion-button [routerLink]="['notes']" style="position: absolute; right: 10px;" fill="clear" color="dark">
Release History
<ion-icon slot="end" name="arrow-forward-outline"></ion-icon>
</ion-button>
</ion-item-divider>
<ion-item lines="none" color="transparent">
<ion-label class="ion-text-wrap" >
<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-text>
<h5>{{ pkg.manifest.description.long }}</h5>
</ion-text>
</ion-label>
</ion-item>
<!-- dependencies -->
<ng-container *ngIf="!(pkg.manifest.dependencies | empty)">
<ion-item-divider>Dependencies</ion-item-divider>
<ion-grid>
<ion-row>
<ion-col *ngFor="let dep of pkg.manifest.dependencies | keyvalue" sizeMd="6" sizeSm="12" sizeXs="12">
<ion-card *ngIf="!dep.value.optional" style="--background: #171717" [routerLink]="['/marketplace', dep.key]">
<ion-item color="transparent" lines="none">
<ion-avatar slot="start">
<img [src]="pkg['dependency-metadata'][dep.key].icon" />
</ion-avatar>
<ion-label class="ion-text-wrap">
<h2>
{{ pkg['dependency-metadata'][dep.key].title }}
<span *ngIf="dep.value.recommended"> (recommended)</span>
</h2>
<p style="font-size: small">{{ dep.value.version | displayEmver }}</p>
</ion-label>
</ion-item>
<ion-card-content>
{{ dep.value.description }}
</ion-card-content>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
</ng-container>
<!-- versions -->
<ion-item-divider>Additional Information</ion-item-divider>
<ion-item lines="none" button (click)="presentAlertVersions()" color="transparent">
<ion-icon color="dark" slot="start" name="file-tray-stacked-outline"></ion-icon>
<ion-label color="dark">Other versions</ion-label>
</ion-item>
</ion-item-group>
</ng-container>
</ion-content>

View File

@@ -0,0 +1,48 @@
.header {
font-family: 'Montserrat';
padding: 2%;
img {
min-width: 16%;
max-width: 18%;
}
.header-text {
margin-left: 5%;
display: inline-block;
vertical-align: top;
.header-title {
line-height: .65;
margin: 0 0 0 -2px;
font-size: calc(20px + 3vw)
}
.header-version {
padding: 12px 0;
margin: 0;
font-size: calc(10px + 1vw)
}
.header-status {
p {
margin: 0;
font-size: calc(6px + 1vw)
}
}
}
}
.recommendation-text {
font-style: italic;
}
.recommendation-error {
color: var(--ion-color-danger);
}
ion-item-divider {
text-transform: uppercase;
margin-top: 22px;
font-weight: 400;
}
#release-notes {
overflow: auto;
max-height: 160px;
}

View File

@@ -1,23 +1,22 @@
import { Component } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { AlertController, ModalController, NavController, PopoverController } from '@ionic/angular'
import { AlertController, ModalController, NavController } from '@ionic/angular'
import { Recommendation } from 'src/app/components/recommendation-button/recommendation-button.component'
import { wizardModal } from 'src/app/components/install-wizard/install-wizard.component'
import { WizardBaker } from 'src/app/components/install-wizard/prebaked-wizards'
import { InformationPopoverComponent } from 'src/app/components/information-popover/information-popover.component'
import { Emver } from 'src/app/services/emver.service'
import { displayEmver } from 'src/app/pipes/emver.pipe'
import { pauseFor } from 'src/app/util/misc.util'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { PackageState } from 'src/app/models/patch-db/data-model'
import { AppAvailableService } from '../app-available.service'
import { MarketplaceService } from '../marketplace.service'
@Component({
selector: 'app-available-show',
templateUrl: './app-available-show.page.html',
styleUrls: ['./app-available-show.page.scss'],
selector: 'marketplace-show',
templateUrl: './marketplace-show.page.html',
styleUrls: ['./marketplace-show.page.scss'],
})
export class AppAvailableShowPage {
export class MarketplaceShowPage {
error = ''
pkgId: string
@@ -26,18 +25,15 @@ export class AppAvailableShowPage {
rec: Recommendation | null = null
showRec = true
depDefinition = '<span style="font-style: italic">Service Dependencies</span> are other services that this service recommends or requires in order to run.'
constructor (
private readonly route: ActivatedRoute,
private readonly alertCtrl: AlertController,
private readonly modalCtrl: ModalController,
private readonly wizardBaker: WizardBaker,
private readonly navCtrl: NavController,
private readonly popoverController: PopoverController,
private readonly emver: Emver,
public readonly patch: PatchDbModel,
public aaService: AppAvailableService,
public marketplaceService: MarketplaceService,
) { }
async ngOnInit () {
@@ -48,38 +44,24 @@ export class AppAvailableShowPage {
async getPkg (version?: string): Promise<void> {
this.pkgId = this.route.snapshot.paramMap.get('pkgId')
try {
await this.aaService.setPkg(this.pkgId, version)
await this.marketplaceService.setPkg(this.pkgId, version)
} catch (e) {
console.error(e)
this.error = e.message
}
}
async presentPopover (information: string, ev: any) {
const popover = await this.popoverController.create({
component: InformationPopoverComponent,
event: ev,
translucent: false,
showBackdrop: true,
backdropDismiss: true,
componentProps: {
information,
},
})
return await popover.present()
}
async presentAlertVersions () {
const alert = await this.alertCtrl.create({
header: 'Versions',
backdropDismiss: false,
inputs: this.aaService.pkgs[this.pkgId].versions.sort((a, b) => -1 * this.emver.compare(a, b)).map(v => {
inputs: this.marketplaceService.pkgs[this.pkgId].versions.sort((a, b) => -1 * this.emver.compare(a, b)).map(v => {
return {
name: v, // for CSS
type: 'radio',
label: displayEmver(v), // appearance on screen
value: v, // literal SEM version value
checked: this.aaService.pkgs[this.pkgId].manifest.version === v,
checked: this.marketplaceService.pkgs[this.pkgId].manifest.version === v,
}
}),
buttons: [
@@ -99,7 +81,7 @@ export class AppAvailableShowPage {
}
async install () {
const { id, title, version, dependencies, alerts } = this.aaService.pkgs[this.pkgId].manifest
const { id, title, version, dependencies, alerts } = this.marketplaceService.pkgs[this.pkgId].manifest
const { cancelled } = await wizardModal(
this.modalCtrl,
this.wizardBaker.install({
@@ -115,7 +97,7 @@ export class AppAvailableShowPage {
}
async update (action: 'update' | 'downgrade') {
const { id, title, version, dependencies, alerts } = this.aaService.pkgs[this.pkgId].manifest
const { id, title, version, dependencies, alerts } = this.marketplaceService.pkgs[this.pkgId].manifest
const value = {
id,
title,

View File

@@ -5,7 +5,7 @@ import { ApiService } from 'src/app/services/api/api.service'
@Injectable({
providedIn: 'root',
})
export class AppAvailableService {
export class MarketplaceService {
pkgs: { [id: string]: AvailableShow } = { }
constructor (

View File

@@ -46,7 +46,7 @@
</h2>
<p>
{{ not['created-at'] | date: 'short' }}
<a *ngIf="not['package-id'] as pkgId" style="text-decoration: none;" [routerLink]="['/services', 'installed', not['package-id']]">
<a *ngIf="not['package-id'] as pkgId" style="text-decoration: none;" [routerLink]="['/services', not['package-id']]">
- {{ not['package-id'] }}
</a>
</p>

View File

@@ -69,7 +69,6 @@
<ion-icon slot="icon-only" name="copy-outline"></ion-icon>
</ion-button>
</ion-item>
<ion-item-divider></ion-item-divider>
</ng-container>
</ion-item-group>

View File

@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { GeneralSettingsPage } from './general-settings.page'
import { PreferencesPage } from './preferences.page'
import { Routes, RouterModule } from '@angular/router'
import { SharingModule } from 'src/app/modules/sharing.module'
import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-back.component.module'
@@ -9,7 +9,7 @@ import { PwaBackComponentModule } from 'src/app/components/pwa-back-button/pwa-b
const routes: Routes = [
{
path: '',
component: GeneralSettingsPage,
component: PreferencesPage,
},
]
@@ -22,7 +22,7 @@ const routes: Routes = [
PwaBackComponentModule,
],
declarations: [
GeneralSettingsPage,
PreferencesPage,
],
})
export class GeneralSettingsPageModule { }
export class PreferencesPageModule { }

View File

@@ -3,7 +3,7 @@
<ion-buttons slot="start">
<pwa-back-button></pwa-back-button>
</ion-buttons>
<ion-title>General Settings</ion-title>
<ion-title>Preferences</ion-title>
</ion-toolbar>
</ion-header>

View File

@@ -3,11 +3,11 @@ import { ServerConfigService } from 'src/app/services/server-config.service'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
@Component({
selector: 'general-settings',
templateUrl: './general-settings.page.html',
styleUrls: ['./general-settings.page.scss'],
selector: 'preferences',
templateUrl: './preferences.page.html',
styleUrls: ['./preferences.page.scss'],
})
export class GeneralSettingsPage {
export class PreferencesPage {
constructor (
private readonly serverConfigService: ServerConfigService,
public readonly patch: PatchDbModel,

View File

@@ -23,8 +23,8 @@ const routes: Routes = [
loadChildren: () => import('./server-logs/server-logs.module').then(m => m.ServerLogsPageModule),
},
{
path: 'settings',
loadChildren: () => import('./general-settings/general-settings.module').then(m => m.GeneralSettingsPageModule),
path: 'preferences',
loadChildren: () => import('./preferences/preferences.module').then(m => m.PreferencesPageModule),
},
{
path: 'wifi',

View File

@@ -7,65 +7,17 @@
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-top ion-padding-bottom">
<ion-content>
<ion-item-divider>Backups</ion-item-divider>
<ion-item [routerLink]="['backup']">
<ion-icon slot="start" name="save-outline"></ion-icon>
<ion-label>Create Backup</ion-label>
</ion-item>
<ion-item-divider>Insights</ion-item-divider>
<ion-item-group>
<ion-item [routerLink]="['specs']">
<ion-icon slot="start" name="information-circle-outline"></ion-icon>
<ion-label>About</ion-label>
</ion-item>
<ion-item [routerLink]="['metrics']">
<ion-icon slot="start" name="pulse"></ion-icon>
<ion-label>Monitor</ion-label>
</ion-item>
<ion-item [routerLink]="['logs']">
<ion-icon slot="start" name="newspaper-outline"></ion-icon>
<ion-label>Logs</ion-label>
</ion-item>
<ion-item-divider>Settings</ion-item-divider>
<ion-item lines="none" [routerLink]="['settings']">
<ion-icon slot="start" name="cog-outline"></ion-icon>
<ion-label>General</ion-label>
</ion-item>
<ion-item [routerLink]="['lan']">
<ion-icon slot="start" name="home-outline"></ion-icon>
<ion-label>LAN</ion-label>
</ion-item>
<ion-item [routerLink]="['wifi']">
<ion-icon slot="start" name="wifi"></ion-icon>
<ion-label>WiFi</ion-label>
</ion-item>
<ion-item lines="none" [routerLink]="['developer']">
<ion-icon slot="start" name="terminal-outline"></ion-icon>
<ion-label>Developer Options</ion-label>
</ion-item>
<ion-item-divider>Power</ion-item-divider>
<ion-item button (click)="presentAlertRestart()">
<ion-icon slot="start" name="reload-outline"></ion-icon>
<ion-label>Restart</ion-label>
</ion-item>
<ion-item button lines="none" (click)="presentAlertShutdown()">
<ion-icon slot="start" name="power"></ion-icon>
<ion-label>Shutdown</ion-label>
</ion-item>
</ion-item-group>
<ion-grid>
<ion-row>
<ion-col *ngFor="let cat of settings | keyvalue : asIsOrder" sizeXs="12" sizeMd="6">
<ion-item-divider>{{ cat.key }}</ion-item-divider>
<ion-item style="cursor: pointer;" button *ngFor="let button of cat.value" (click)="button.action()">
<ion-icon slot="start" [name]="button.icon"></ion-icon>
<ion-label>{{ button.title }}</ion-label>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>

View File

@@ -1,12 +0,0 @@
.notification-button {
ion-badge {
position: absolute;
font-size: 8px;
bottom: .7rem;
left: .8rem;
}
}
ion-item-divider {
margin-top: 0px;
}

View File

@@ -1,10 +1,11 @@
import { Component } from '@angular/core'
import { LoadingOptions } from '@ionic/core'
import { AlertController, ModalController } from '@ionic/angular'
import { AlertController, NavController } from '@ionic/angular'
import { ApiService } from 'src/app/services/api/api.service'
import { LoaderService } from 'src/app/services/loader.service'
import { PatchDbModel } from 'src/app/models/patch-db/patch-db-model'
import { ServerStatus } from 'src/app/models/patch-db/data-model'
import { ActivatedRoute } from '@angular/router'
@Component({
selector: 'server-show',
@@ -13,15 +14,21 @@ import { ServerStatus } from 'src/app/models/patch-db/data-model'
})
export class ServerShowPage {
ServerStatus = ServerStatus
settings: ServerSettings = { }
constructor (
private readonly alertCtrl: AlertController,
private readonly loader: LoaderService,
private readonly apiService: ApiService,
private readonly modalCtrl: ModalController,
private readonly navCtrl: NavController,
private readonly route: ActivatedRoute,
public readonly patch: PatchDbModel,
) { }
ngOnInit () {
this.setButtons()
}
async presentAlertRestart () {
const alert = await this.alertCtrl.create({
backdropDismiss: false,
@@ -73,7 +80,7 @@ export class ServerShowPage {
// this.serverModel.markUnreachable()
await this.apiService.restartServer({ })
})
.catch(e => this.setError(e))
.catch(console.error)
}
private async shutdown () {
@@ -83,11 +90,74 @@ export class ServerShowPage {
// this.serverModel.markUnreachable()
await this.apiService.shutdownServer({ })
})
.catch(e => this.setError(e))
.catch(console.error)
}
setError (e: Error) {
console.error(e)
private setButtons (): void {
this.settings = {
'Settings': [
{
title: 'Preferences',
icon: 'cog-outline',
action: () => this.navCtrl.navigateForward(['preferences'], { relativeTo: this.route }),
},
{
title: 'LAN',
icon: 'home-outline',
action: () => this.navCtrl.navigateForward(['lan'], { relativeTo: this.route }),
},
{
title: 'WiFi',
icon: 'wifi',
action: () => this.navCtrl.navigateForward(['wifi'], { relativeTo: this.route }),
},
{
title: 'Developer Options',
icon: 'terminal-outline',
action: () => this.navCtrl.navigateForward(['developer'], { relativeTo: this.route }),
},
],
'Insights': [
{
title: 'About',
icon: 'information-circle-outline',
action: () => this.navCtrl.navigateForward(['specs'], { relativeTo: this.route }),
},
{
title: 'Monitor',
icon: 'pulse',
action: () => this.navCtrl.navigateForward(['metrics'], { relativeTo: this.route }),
},
{
title: 'Logs',
icon: 'newspaper-outline',
action: () => this.navCtrl.navigateForward(['logs'], { relativeTo: this.route }),
},
],
'Backups': [
{
title: 'Create Backup',
icon: 'save-outline',
action: () => this.navCtrl.navigateForward(['backup'], { relativeTo: this.route }),
},
],
'Power': [
{
title: 'Restart',
icon: 'reload-outline',
action: () => this.presentAlertRestart(),
},
{
title: 'Shutdown',
icon: 'power',
action: () => this.presentAlertShutdown(),
},
],
}
}
asIsOrder () {
return 0
}
}
@@ -100,4 +170,10 @@ const LoadingSpinner: (m?: string) => LoadingOptions = (m) => {
} as LoadingOptions
}
interface ServerSettings {
[key: string]: {
title: string
icon: string
action: Function
}[]
}

View File

@@ -469,7 +469,7 @@ export class MockApiService extends ApiService {
async getMarketplaceData (params: RR.GetMarketplaceDataReq): Promise<RR.GetMarketplaceDataRes> {
await pauseFor(2000)
return {
categories: ['featured', 'bitcoin', 'lightning', 'data', 'messaging'],
categories: ['featured', 'bitcoin', 'lightning', 'data', 'messaging', 'social', 'alt coin'],
}
}

View File

@@ -20,7 +20,7 @@ export module Mock {
},
{
id: 'lnd',
title: 'Lightning Network Daemon',
title: 'LND',
version: '0.11.1',
descriptionShort: 'A BOLT-compliant, lightning network node.',
icon: 'assets/img/service-icons/lnd.png',
@@ -157,7 +157,7 @@ export module Mock {
export const MockManifestLnd: Manifest = {
id: 'lnd',
title: 'Lightning Network Daemon',
title: 'LND',
version: '0.11.1',
description: {
short: 'A bolt spec compliant client.',

View File

@@ -20,7 +20,7 @@ function handleInstalledState (status: Status): PkgStatusRendering {
}
if (Object.keys(status['dependency-errors']).length) {
return { display: 'Dependency Issue', color: 'warning', showDots: false, feStatus: FEStatus.DependencyIssue }
return { display: 'Needs Attention', color: 'warning', showDots: false, feStatus: FEStatus.DependencyIssue }
}
switch (status.main.status) {
@@ -36,7 +36,7 @@ function handleRunningState (status: MainStatusRunning): PkgStatusRendering {
if (Object.values(status.health).some(h => h.result === 'failure')) {
return { display: 'Needs Attention', color: 'danger', showDots: false, feStatus: FEStatus.NeedsAttention }
} else if (Object.values(status.health).some(h => h.result === 'starting')) {
return { display: 'Starting Up', color: 'warning', showDots: true, feStatus: FEStatus.Starting }
return { display: 'Starting', color: 'warning', showDots: true, feStatus: FEStatus.Starting }
} else if (Object.values(status.health).some(h => h.result === 'loading')) {
const firstLoading = Object.values(status.health).find(h => h.result === 'loading') as HealthCheckResultLoading
return { display: firstLoading.message, color: 'warning', showDots: true, feStatus: FEStatus.Loading }

View File

@@ -183,7 +183,7 @@ export class StartupAlertsNotifier {
{
text: 'View in Marketplace',
handler: () => {
return this.navCtrl.navigateForward('/services/marketplace').then(() => resolve(false))
return this.navCtrl.navigateForward('/marketplace').then(() => resolve(false))
},
},
],

View File

@@ -117,9 +117,12 @@
white-space: normal !important;
}
ion-card-title {
font-family: 'Montserrat';
}
ion-title {
font-family: 'Montserrat';
font-weight: unset;
}
ion-textarea {
@@ -272,11 +275,6 @@ ion-avatar {
--border-radius: var(--icon-border-radius);
}
.no-white-space {
--white-space: 0;
--box-shadow: 3px 3px 10px var(--ion-color-primary);
}
.notifier-item {
margin: 12px;
margin-top: 0px;
@@ -301,7 +299,6 @@ ion-item-divider {
color: var(--ion-color-medium);
font-size: medium;
padding-left: 10px;
font-weight: unset;
}
.dots {