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)
+}