From ee9c3286062512c6ec3249e979bba5102d82b972 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 7 Mar 2023 19:48:49 -0700 Subject: [PATCH] begin popover for UI launch select --- .../app-interfaces/app-interfaces.module.ts | 9 ++-- .../app-interfaces/app-interfaces.page.ts | 36 +-------------- .../app-list-pkg/app-list-pkg.component.html | 27 +++++++++++- .../app-list-pkg/app-list-pkg.component.scss | 3 ++ .../app-list-pkg/app-list-pkg.component.ts | 21 +++++---- .../projects/ui/src/app/pipes/ui/ui.module.ts | 6 +-- .../projects/ui/src/app/pipes/ui/ui.pipe.ts | 44 +++++++++++++++++++ 7 files changed, 93 insertions(+), 53 deletions(-) create mode 100644 frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.scss diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.module.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.module.ts index 0a8781a1a..4826e67fa 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.module.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.module.ts @@ -6,8 +6,8 @@ import { SharedPipesModule } from '@start9labs/shared' import { AppInterfacesItemComponent, AppInterfacesPage, - AddressTypePipe, } from './app-interfaces.page' +import { UiPipeModule } from 'src/app/pipes/ui/ui.module' const routes: Routes = [ { @@ -22,11 +22,8 @@ const routes: Routes = [ IonicModule, RouterModule.forChild(routes), SharedPipesModule, + UiPipeModule, ], - declarations: [ - AppInterfacesPage, - AppInterfacesItemComponent, - AddressTypePipe, - ], + declarations: [AppInterfacesPage, AppInterfacesItemComponent], }) export class AppInterfacesPageModule {} diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts index c3f46d466..7c399f052 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.ts @@ -1,10 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - Input, - Pipe, - PipeTransform, -} from '@angular/core' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { ModalController, ToastController } from '@ionic/angular' import { getPkgId, copyToClipboard } from '@start9labs/shared' @@ -80,31 +74,3 @@ export class AppInterfacesItemComponent { await toast.present() } } - -@Pipe({ - name: 'addressType', -}) -export class AddressTypePipe implements PipeTransform { - transform(address: string): string { - if (isValidIpv4(address)) return 'IPv4' - if (isValidIpv6(address)) return 'IPv6' - - const hostname = new URL(address).hostname - if (hostname.endsWith('.onion')) return 'Tor' - if (hostname.endsWith('.local')) return 'Local' - - return 'Custom' - } -} - -function isValidIpv4(address: string): boolean { - const regexExp = - /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ - return regexExp.test(address) -} - -function isValidIpv6(address: string): boolean { - const regexExp = - /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi - return regexExp.test(address) -} diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html index ba122782d..412d2016a 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html @@ -25,10 +25,35 @@ slot="end" fill="clear" color="primary" - (click)="launchUi($event, installed['address-info'])" + (click)="openPopover($event)" [disabled]="status !== 'running'" > + + + + + + {{ uiAddress.name }} + + +

{{ address | addressType }}

+

{{ address }}

+
+
+
+
+
+
+
diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.scss b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.scss new file mode 100644 index 000000000..64a03813a --- /dev/null +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.scss @@ -0,0 +1,3 @@ +ion-popover { + --min-width: 300px; +} \ No newline at end of file diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts index 945283386..3a5bfc4c6 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts @@ -1,21 +1,25 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { - InstalledPackageInfo, - PackageMainStatus, -} from 'src/app/services/patch-db/data-model' + ChangeDetectionStrategy, + Component, + Input, + ViewChild, +} from '@angular/core' +import { PackageMainStatus } from 'src/app/services/patch-db/data-model' import { PkgInfo } from 'src/app/util/get-package-info' -import { UiLauncherService } from 'src/app/services/ui-launcher.service' @Component({ selector: 'app-list-pkg', templateUrl: 'app-list-pkg.component.html', + styleUrls: ['app-list-pkg.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppListPkgComponent { + @ViewChild('popover') popover!: HTMLIonPopoverElement + @Input() pkg!: PkgInfo - constructor(private readonly launcherService: UiLauncherService) {} + isPopoverOpen = false get status(): PackageMainStatus { return ( @@ -23,9 +27,10 @@ export class AppListPkgComponent { ) } - launchUi(e: Event, addressInfo: InstalledPackageInfo['address-info']): void { + openPopover(e: Event): void { e.stopPropagation() e.preventDefault() - this.launcherService.launch(addressInfo) + this.popover.event = e + this.isPopoverOpen = true } } diff --git a/frontend/projects/ui/src/app/pipes/ui/ui.module.ts b/frontend/projects/ui/src/app/pipes/ui/ui.module.ts index 9637306de..1ae23b713 100644 --- a/frontend/projects/ui/src/app/pipes/ui/ui.module.ts +++ b/frontend/projects/ui/src/app/pipes/ui/ui.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core' -import { UiPipe } from './ui.pipe' +import { UiPipe, UiAddressesPipe, AddressTypePipe } from './ui.pipe' @NgModule({ - declarations: [UiPipe], - exports: [UiPipe], + declarations: [UiPipe, UiAddressesPipe, AddressTypePipe], + exports: [UiPipe, UiAddressesPipe, AddressTypePipe], }) export class UiPipeModule {} diff --git a/frontend/projects/ui/src/app/pipes/ui/ui.pipe.ts b/frontend/projects/ui/src/app/pipes/ui/ui.pipe.ts index 03718360e..f8d03a81b 100644 --- a/frontend/projects/ui/src/app/pipes/ui/ui.pipe.ts +++ b/frontend/projects/ui/src/app/pipes/ui/ui.pipe.ts @@ -10,3 +10,47 @@ export class UiPipe implements PipeTransform { return hasUi(addressInfo) } } + +@Pipe({ + name: 'uiAddresses', +}) +export class UiAddressesPipe implements PipeTransform { + transform( + addressInfo: InstalledPackageInfo['address-info'], + ): { name: string; addresses: string[] }[] { + return Object.values(addressInfo) + .filter(info => info.ui) + .map(info => ({ + name: info.name, + addresses: info.addresses, + })) + } +} + +@Pipe({ + name: 'addressType', +}) +export class AddressTypePipe implements PipeTransform { + transform(address: string): string { + if (isValidIpv4(address)) return 'IPv4' + if (isValidIpv6(address)) return 'IPv6' + + const hostname = new URL(address).hostname + if (hostname.endsWith('.onion')) return 'Tor' + if (hostname.endsWith('.local')) return 'Local' + + return 'Custom' + } +} + +function isValidIpv4(address: string): boolean { + const regexExp = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ + return regexExp.test(address) +} + +function isValidIpv6(address: string): boolean { + const regexExp = + /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi + return regexExp.test(address) +}